# Get Query Results
Source: https://moengage.com/docs/api/analytics-queries/get-query-results
/api/analytics-query/analytics-query.yaml get /v5/analytics/query/{request_id}/results
Returns the resolved results of a completed query, as an array of metric rows.
Every row carries the common fields (`metric`, `granularity`, `splitby`, `tseq`, `cseq`). The remaining fields depend on the analysis type the query was registered for, so handle each row as a flexible set of keys. Select an example below to see the shape returned for each analysis type.
# Get Query Status
Source: https://moengage.com/docs/api/analytics-queries/get-query-status
/api/analytics-query/analytics-query.yaml get /v5/analytics/query/{request_id}/status
Returns the execution status of a registered query. Poll this endpoint until the query reaches a terminal status, then fetch the results.
# Register a Behavior Query
Source: https://moengage.com/docs/api/analytics-queries/register-a-behavior-query
/api/analytics-query/analytics-query.yaml post /v5/analytics/behavior
Registers an asynchronous Behavior analysis query and returns a `request_id`. Behavior analysis measures how many users perform one or more events over a time range, broken down by the dimensions you choose.
Use the `request_id` with [Get Query Status](/api/analytics-queries/get-query-status) to poll for completion, then [Get Query Results](/api/analytics-queries/get-query-results) to fetch the resolved series.
#### Rate Limit
The rate limits are at the workspace level. A maximum of 5 requests per second, 20 requests per minute, and 350 requests per hour are allowed per workspace.
# Register a Funnels Query
Source: https://moengage.com/docs/api/analytics-queries/register-a-funnels-query
/api/analytics-query/analytics-query.yaml post /v5/analytics/funnels
Registers an asynchronous Funnels analysis query and returns a `request_id`. Funnels analysis measures step-by-step conversion and drop-off across an ordered sequence of events.
Use the `request_id` with [Get Query Status](/api/analytics-queries/get-query-status) to poll for completion, then [Get Query Results](/api/analytics-queries/get-query-results) to fetch the resolved series.
#### Rate Limit
The rate limits are at the workspace level. A maximum of 5 requests per second, 20 requests per minute, and 250 requests per hour are allowed per workspace.
# Register a Retention Query
Source: https://moengage.com/docs/api/analytics-queries/register-a-retention-query
/api/analytics-query/analytics-query.yaml post /v5/analytics/retention
Registers an asynchronous Retention analysis query and returns a `request_id`. Retention analysis groups users who performed a first event into cohorts, then measures how many return to perform a second event in each later period.
Use the `request_id` with [Get Query Status](/api/analytics-queries/get-query-status) to poll for completion, then [Get Query Results](/api/analytics-queries/get-query-results) to fetch the resolved series.
#### Rate Limit
The rate limits are at the workspace level. A maximum of 5 requests per second, 10 requests per minute, and 50 requests per hour are allowed per workspace.
# Register a Session-Source Query
Source: https://moengage.com/docs/api/analytics-queries/register-a-session-source-query
/api/analytics-query/analytics-query.yaml post /v5/analytics/session-source
Registers an asynchronous Session/Source analysis query and returns a `request_id`. Session/Source analysis reports session count or average session duration, broken down by acquisition attributes such as source, medium, and campaign.
Use the `request_id` with [Get Query Status](/api/analytics-queries/get-query-status) to poll for completion, then [Get Query Results](/api/analytics-queries/get-query-results) to fetch the resolved series.
#### Rate Limit
The rate limits are at the workspace level. A maximum of 5 requests per second, 20 requests per minute, and 50 requests per hour are allowed per workspace.
# Register a User Analysis Query
Source: https://moengage.com/docs/api/analytics-queries/register-a-user-analysis-query
/api/analytics-query/analytics-query.yaml post /v5/analytics/user-analysis
Registers an asynchronous User Property Analysis (UPA) query and returns a `request_id`. User Property Analysis computes counts, distinct-value counts, distributions, and aggregations over user attributes for a cohort of users.
Use the `request_id` with [Get Query Status](/api/analytics-queries/get-query-status) to poll for completion, then [Get Query Results](/api/analytics-queries/get-query-results) to fetch the resolved series.
#### Rate Limit
The rate limits are at the workspace level. A maximum of 5 requests per second, 15 requests per minute, and 100 requests per hour are allowed per workspace.
# Analytics Query APIs Overview
Source: https://moengage.com/docs/api/analytics-query/analytics-query-overview
Run MoEngage Analytics queries — Behavior, Funnels, Retention, Session/Source, and User Property Analysis — and fetch their results programmatically.
The Analytics Query APIs let you run MoEngage's analysis queries programmatically and retrieve their results. They cover the same analyses available in the MoEngage dashboard: Behavior, Funnels, Retention, Session/Source (BFRS), and User Property Analysis (UPA).
These queries are asynchronous. A `POST` registers the query and returns a `request_id` immediately; the query runs in the background; you then poll for status and fetch the results.
## Endpoints
The Analytics Query APIs include the following endpoints:
* [Register a Behavior Query](/docs/api/analytics-queries/register-a-behavior-query): Run a Behavior analysis.
* [Register a Funnels Query](/docs/api/analytics-queries/register-a-funnels-query): Run a Funnels analysis.
* [Register a Retention Query](/docs/api/analytics-queries/register-a-retention-query): Run a Retention analysis.
* [Register a Session-Source Query](/docs/api/analytics-queries/register-a-session-source-query): Run a Session/Source analysis.
* [Register a User Analysis Query](/docs/api/analytics-queries/register-a-user-analysis-query): Run a User Property Analysis.
* [Get Query Status](/docs/api/analytics-queries/get-query-status): Check the execution status of a registered query.
* [Get Query Results](/docs/api/analytics-queries/get-query-results): Fetch the resolved results of a completed query.
## Typical Workflow
Each analysis follows the same submit, poll, and fetch sequence:
Call one of the analysis endpoints, for example [Register a Behavior Query](/docs/api/analytics-queries/register-a-behavior-query). The response echoes the analysis `type` and returns a `request_id`.
Call [Get Query Status](/docs/api/analytics-queries/get-query-status) with that `request_id`. The query is still running while `status` is `PENDING` or `PROCESSING`, so keep polling until it reaches a final status of `SUCCESSFUL` or `FAILED`. When a query fails, the response also returns a `failure_reason`.
Call [Get Query Results](/docs/api/analytics-queries/get-query-results) with the same `request_id` to retrieve the results. The shape of the `data` array depends on the analysis type.
## FAQs
Completion time depends on the time range, the number of events, and the volume of data scanned. Poll [Get Query Status](/docs/api/analytics-queries/get-query-status) until the status is `SUCCESSFUL`, then fetch the results. Avoid polling in a tight loop; leave a short interval between calls.
No. A `request_id` identifies one query execution. To run the analysis again, register a new query and use the new `request_id`.
A `428` means the workspace has reached its monthly Fair Usage Policy (FUP) limit for analytics usage. Analytics queries are blocked for the rest of the billing cycle. Contact your Customer Success Manager to expand your quota.
The rows returned depend on the analysis type the query was registered for. Behavior, Funnels, Retention, Session/Source, and User Analysis each add their own fields on top of the common ones. Handle each row as a flexible set of keys. See [Get Query Results](/docs/api/analytics-queries/get-query-results) for an example of each shape.
Each analysis enforces limits on how many events, segments, and breakdowns a single query can include. The error message names the field that failed. Check the description of that field on the endpoint page for its accepted values and limits.
These endpoints run the same analyses as the Analyze section of the MoEngage dashboard, scoped to the authenticated workspace. To read data from saved dashboard charts instead of running a new query, use the [Custom Dashboards APIs](/docs/api/analytics/analytics-overview).
## Postman Collection
Test these endpoints quickly using our pre-configured Postman collection: [View MoEngage Analytics Query APIs Collection](https://www.postman.com/moengage-dev/api-docs/collection/xk31arr/moengage-analytics-query-api-s).
# Custom Dashboards Overview
Source: https://moengage.com/docs/api/analytics/analytics-overview
Programmatically access MoEngage Custom Dashboards and fetch the analytics data behind their charts.
The MoEngage Custom Dashboards APIs give you read-only, programmatic access to your [Custom Dashboards](/docs/user-guide/analyze/dashboards/custom-dashboards) and the analysis charts they contain. Use these endpoints to discover the dashboards in your workspace, retrieve a dashboard's structure, and fetch the data behind each chart. You can then pull MoEngage analytics into external tools, scheduled reports, and automated workflows.
All endpoints are `GET` requests and do not modify any data.
## Endpoints
The Custom Dashboards APIs include the following endpoints:
* [List Dashboards](/docs/api/dashboards/list-dashboards): Retrieve the dashboards accessible to your workspace.
* [Get Dashboard Charts](/docs/api/dashboards/get-dashboard-charts): Retrieve a dashboard's details and the list of charts on it.
* [Get Chart Data](/docs/api/dashboards/get-chart-data): Fetch the data for a single chart.
## Typical Workflow
The three endpoints are designed to be used in sequence:
1. Call **List Dashboards** to find the dashboard you need and note its `_id`.
2. Call **Get Dashboard Charts** with that dashboard ID to list its charts and note each chart's `_id`.
3. Call **Get Chart Data** with the dashboard ID and a chart ID to fetch the underlying data.
## Authentication
Authentication is done via Basic Auth. This requires a Base64-encoded string of your credentials in the format `username:password`.
* **Username**: Use your MoEngage Workspace ID (also known as the App ID). Find it in the MoEngage dashboard at **Settings** > **Account** > **API keys**.
* **Password**: Use an API key from **Settings** > **Account** > **API keys**.
Refer to [API Key Dashboard](/docs/user-guide/settings/account/api-and-api-keys/api-key-dashboard) for details on creating and managing API keys.
When creating an API key for this API, ensure the **Dashboard & Analyze** checkbox is selected under **Select APIs for access**.
## Access and Permissions
These endpoints return only workspace-level (public) dashboards. Private dashboards are not available through this API. Archived dashboards are also excluded. For more information on dashboard visibility and sharing, refer to [Custom Dashboards](/docs/user-guide/analyze/dashboards/custom-dashboards).
## FAQs
These APIs cover Custom Dashboards. MoEngage's default dashboards are not available through these endpoints. This includes Inbuilt Lifecycle Engagement (ILE), Campaign Stats, Best Time to Send, and Reachability.
A dashboard is excluded if it is archived or if it is a private dashboard. Only workspace-level (public) dashboards are returned.
Chart data reflects the chart's saved settings, such as its date range, segment, filters, and breakdowns. The endpoint is read-only and does not accept date-range or segment parameters, so to change what a chart returns, edit and save the chart in the MoEngage dashboard.
Chart data is served from a server-side cache by default. To recompute a chart, call [Get Chart Data](/docs/api/dashboards/get-chart-data) with the `cache` query parameter set to `false`.
The fields in each row depend on the chart's analysis type: Behavior, Funnels, Retention, User, or Session and Source. A chart can return fields beyond the common ones, so handle the response as a flexible set of keys per row.
## Postman Collection
Test these endpoints quickly using our pre-configured Postman collection: [View MoEngage Analytics Custom Dashboard APIs Collection](https://www.postman.com/moengage-dev/api-docs/collection/rjb0r1e/moengage-analytics-custom-dashboard-apis).
# Bulk Import Users and Events
Source: https://moengage.com/docs/api/bulk/bulk-import-users-and-events
/api/data/data.yaml post /transition/{Workspace_ID}
The Bulk Import API sends multiple user and event requests in batch to MoEngage, using a single API request. You can send a batch request of a maximum of 100 KB in a single API call.
All bulk API requests return a 200 response code. Debugging should be done on the user profile on the dashboard.
#### User Identity Resolution
In MoEngage, data ingestion uses an ID to create or update a user. For workspaces in MoEngage with [Identity Resolution](/docs/user-guide/data/user-data/unified-identity-identity-resolution) enabled, you can use the Bulk Import API to create or update users using a specific identifier, such as a mobile number or email ID. These identifiers must be enabled for the workspace in the Identity Resolution dashboard.
You can:
* Create users through Server-to-Server Data APIs even when they do not have an ID (but have other identifiers).
* Create a user or track events of a user when identifiers other than ID (for example, email ID or phone number) are known.
#### Rate Limit
A single bulk import API contains users, devices, and events together. Send a maximum of 60,000 users and 60,000 events per minute across all API requests.
# Business Events API (Legacy): Overview
Source: https://moengage.com/docs/api/business-events/business-events-legacy/business-events-overview
Create, manage, and trigger real-time business events to power automated MoEngage campaigns.
The MoEngage Business Events API allows you to create and trigger specialized events that represent business occurrences—such as a flight delay, a price drop on a watched item, or the release of a new OTT series episode. These events act as triggers for **Business Event Triggered Campaigns**, allowing you to automate high-context communication based on external data points rather than just user behavior.
These endpoints remain supported for existing integrations. For new integrations, use the [Business Events API (V5)](/docs/api/business-events/business-events-v5/business-events-v5-overview), which covers triggering and looking up business events through the unified MoEngage gateway. Creating a business event is available only on the legacy API.
## Endpoints
The Business Events API (Legacy) is a collection of the following endpoints:
* [Create Business Event](/docs/api/business-events/create-business-event): Define the schema and attributes for a new event.
* [Trigger Business Event](/docs/api/business-events/trigger-business-event): Trigger an event to initiate associated campaigns.
* [Search Business Events](/docs/api/business-events/search-business-events): Retrieve details of existing events using IDs or names.
## FAQs
### Manage Business Events
The supported data types include integer, float, string, date, and array.
No, business event names must be unique within your workspace.
Use the [List Business Events API](/docs/api/business-events/list-business-events) with no `name` or `id` parameter, or the [Search Business Events (V5) API](/docs/api/business-events/search-business-events-v5) with an empty body, to fetch a paginated list of every business event in your workspace. To look up several specific events at once by ID or name, use Search Business Events (V5). The legacy [Search Business Events API](/docs/api/business-events/search-business-events) remains supported for existing integrations.
### Trigger Campaigns
No, it is not mandatory to pass all defined attributes when triggering the event. Only include the attributes required for your campaign personalization.
Navigate to **Engage -> Campaigns** on the MoEngage Dashboard and search for the campaign associated with your Business Event to view real-time analytics and trigger counts.
The API will return a `400 Bad Request` error with the message "No active campaign found for business event name." Ensure your campaign is in the **Active** state before triggering.
## Postman Collection
Test these endpoints quickly using our pre-configured Postman collection: [View MoEngage Business Events Collection](https://www.postman.com/moengage-dev/workspace/api-docs/collection/3182294-38587f83-f039-46f3-b86e-10af2c918053)
# Business Events API (V5): Overview
Source: https://moengage.com/docs/api/business-events/business-events-v5/business-events-v5-overview
Trigger real-time business events through the unified MoEngage gateway, and look up the events registered in your workspace.
The MoEngage Business Events API (V5) lets you fire a business event that is already registered in your workspace — such as a flight delay, a price drop on a watched item, or the release of a new OTT series episode. Firing the event enqueues every active campaign and flow attached to it for delivery, so you can automate high-context communication based on external data points rather than just user behavior. The V5 endpoints also let you look up the business events that exist in your workspace.
These endpoints are served through the unified MoEngage gateway behind a versioned `{response_id, type, data}` envelope. To create a new business event, use the [Business Events (Legacy)](/docs/api/business-events/business-events-legacy/business-events-overview) API — that operation has not moved to V5 yet.
## Endpoints
The Business Events API (V5) is a collection of the following endpoints:
* [Trigger Business Event (V5)](/docs/api/business-events/trigger-business-event-v5): Fire a registered business event to enqueue its attached campaigns and flows.
* [List Business Events](/docs/api/business-events/list-business-events): List all business events in the workspace, or look up a single one by name or ID. Returns campaign and trigger counts, and supports cursor-based pagination.
* [Search Business Events (V5)](/docs/api/business-events/search-business-events-v5): List all business events, or look up several at once by names or IDs. Returns the same fields as List Business Events and supports cursor-based pagination.
## Authentication
Authentication is done via Basic Auth. This requires a Base64-encoded string of your credentials in the format `username:password`.
* **Username**: Use your MoEngage Workspace ID (also known as the App ID). Find it in the MoEngage dashboard at **Settings** > **Account** > **API keys**.
* **Password**: Use an API key from **Settings** > **Account** > **API keys**.
Refer to [API Key Dashboard](/docs/user-guide/settings/account/api-and-api-keys/api-key-dashboard) for details on creating and managing API keys.
## FAQs
### Trigger Business Events
No. It isn't mandatory to send every attribute defined on the business event. Only include the attributes required for the personalization used by your campaigns and flows.
The event name is validated against the events registered in your workspace. Make sure `event_name` matches an event already created via the [Create Business Event](/docs/api/business-events/create-business-event) API before triggering it.
`triggered_status` summarizes the outcome of the trigger: `SUCCESS` (every attached campaign or flow was reserved), `PARTIAL_SUCCESS` (at least one reserved, at least one skipped by quota), or `FAILURE` (nothing reserved — either all were skipped by quota, or the event has no active campaigns or flows attached).
Check `triggered_campaign_ids` and `triggered_flow_ids` in the response `data` object — these list the IDs that were reserved for this firing. `failed_campaign_ids` and `failed_flow_ids` list anything skipped due to quota.
Navigate to **Engage -> Campaigns** on the MoEngage Dashboard and search for the campaign associated with your business event to view real-time analytics and trigger counts.
### Look Up Business Events
Use [List Business Events](/docs/api/business-events/list-business-events) to browse everything, or to look up one event by an exact `name` or `id` passed as query parameters. Use [Search Business Events (V5)](/docs/api/business-events/search-business-events-v5) to look up several events at once, since `filters.names` and `filters.ids` accept arrays. Both return the same fields and list everything when you supply no filter.
No. Provide either `filters.names` or `filters.ids`, not both. Supplying both returns a `400` error. The same rule applies to the `name` and `id` query parameters on List Business Events.
Both endpoints return at most 20 events per page. When `pagination.has_more` is `true`, pass `pagination.next_cursor` back as `cursor` to fetch the next page. Treat the cursor as opaque — do not decode or modify it.
The V5 endpoints report attribute types as `string`, `number`, `boolean`, or `datetime`. The legacy [Create Business Event](/docs/api/business-events/create-business-event) API accepts `string`, `int`, `float`, `array`, and `date`. Map between the two when you move an integration to V5.
`trigger_count` is the total number of times the event has been triggered. `campaign_count` is the cumulative number of child campaigns those triggers have launched across all firings.
## Postman Collection
Test these endpoints using our pre-configured Postman collection: [View MoEngage Business Events Collection](https://www.postman.com/moengage-dev/api-docs/collection/k48uulb/moengage-business-events-api-v5?action=share\&source=copy-link\&creator=3486165).
# Create Business Event
Source: https://moengage.com/docs/api/business-events/create-business-event
/api/business-events/business-events-legacy/business-events.yaml post /business_event
This API creates business events in MoEngage. You can use these events to trigger campaigns whenever they occur. In MoEngage, you can set up event-triggered campaigns to notify users about new episodes, flight delays, or price reductions on items they have viewed, wished for, or added to their carts.
#### Rate Limit
The rate limits are at the workspace level. You can create a maximum of 50 business events for each workspace.
# List Business Events
Source: https://moengage.com/docs/api/business-events/list-business-events
/api/business-events/business-events-v5/business-events-v5.yaml get /v5/business-events
Returns all business events in the workspace, or a single one when you supply `name` or `id`. Use either `name` or `id`, not both. Omit both parameters to list everything (paginated).
Use this endpoint to page through every event or to look up exactly one. To look up several events at once, use [Search Business Events (V5)](/api/business-events/search-business-events-v5), which takes arrays of names or IDs.
#### Rate Limits
The rate limits are at the workspace level. A maximum of 60 requests per minute and 1,000 requests per hour are allowed per workspace.
#### Example Requests
```bash List the First Page
curl --request GET \
--url 'https://api-01.moengage.com/v5/business-events?limit=20' \
--header 'Authorization: Basic ' \
--header 'X-MOE-Request-Id: 550e8400-e29b-41d4-a716-446655440000'
```
```bash Look Up a Single Event by Name
curl --request GET \
--url 'https://api-01.moengage.com/v5/business-events?name=price_drop' \
--header 'Authorization: Basic '
```
```bash Fetch the Next Page
curl --request GET \
--url 'https://api-01.moengage.com/v5/business-events?limit=20&cursor=eyJsYXN0X2lkIjoiNjdhMmM0ZjE4ZDNiNWUwYTljMWY0ZTc0In0=' \
--header 'Authorization: Basic '
```
# Search Business Events
Source: https://moengage.com/docs/api/business-events/search-business-events
/api/business-events/business-events-legacy/business-events.yaml post /business_event/search
This API searches for business events by specifying their event IDs.
#### Rate Limit
The rate limit is 100 RPM.
# Search Business Events (V5)
Source: https://moengage.com/docs/api/business-events/search-business-events-v5
/api/business-events/business-events-v5/business-events-v5.yaml post /v5/business-events/search
Returns all business events in the workspace, or the subset matching a filter. Provide either `names` or `ids` inside `filters` (not both). Omit the body (or `filters`) to list everything (paginated).
Use this endpoint to look up several events in one call. To page through every event or to fetch exactly one, [List Business Events](/api/business-events/list-business-events) does the same job with a `GET`. Both return the same object and the same pagination.
This endpoint uses `POST` so that multi-valued filters travel as JSON arrays — a name may contain any character (for example, a comma), which a comma-delimited query string could not represent unambiguously.
#### Rate Limits
The rate limits are at the workspace level. A maximum of 30 requests per minute and 500 requests per hour are allowed per workspace.
#### Example Requests
```bash Search by Names
curl --request POST \
--url 'https://api-01.moengage.com/v5/business-events/search' \
--header 'Authorization: Basic ' \
--header 'Content-Type: application/json' \
--data '{
"filters": {
"names": ["price_drop", "cart_abandoned"]
},
"limit": 20
}'
```
```bash Search by IDs
curl --request POST \
--url 'https://api-01.moengage.com/v5/business-events/search' \
--header 'Authorization: Basic ' \
--header 'Content-Type: application/json' \
--data '{
"filters": {
"ids": ["67a2c4f18d3b5e0a9c1f4e73", "67a2c4f18d3b5e0a9c1f4e74"]
},
"limit": 20
}'
```
```bash List Everything
curl --request POST \
--url 'https://api-01.moengage.com/v5/business-events/search' \
--header 'Authorization: Basic ' \
--header 'Content-Type: application/json' \
--data '{ "limit": 20 }'
```
# Trigger Business Event
Source: https://moengage.com/docs/api/business-events/trigger-business-event
/api/business-events/business-events-legacy/business-events.yaml post /business_event/trigger
This API triggers a business event in MoEngage. You can set up campaigns to be executed when these events are triggered.
#### Rate Limit
For Campaigns:
* You can send a maximum of 10 triggers per 5 minutes.
* You can send a maximum of 50 triggers per hour.
* You can send a maximum of 200 triggers per day.
For Flows:
* You can trigger a maximum of 3 Business Trigger flows per hour.
* You can trigger a maximum of 10 Business Trigger flows per day.
# Trigger Business Event (V5)
Source: https://moengage.com/docs/api/business-events/trigger-business-event-v5
/api/business-events/business-events-v5/business-events-v5.yaml post /v5/business-events/triggers
Fires a registered business event, enqueueing all attached active campaigns and flows for delivery, subject to per-workspace daily and hourly quotas.
- An unknown event name returns a `400 Bad Request`.
- Attribute types are validated against the registered event schema.
- Delivery is asynchronous.
- The response reports how many attached campaigns and flows were triggered compared to how many were skipped due to quota (`failed`), and returns the triggered campaign and flow IDs.
#### Rate Limits
For campaigns:
* You can send a maximum of 10 triggers per 5 minutes.
* You can send a maximum of 50 triggers per hour.
* You can send a maximum of 200 triggers per day.
For flows:
* You can trigger a maximum of 3 business trigger flows per hour.
* You can trigger a maximum of 10 business trigger flows per day.
If you need higher limits, reach out to your Customer Success Manager (CSM) or MoEngage Support team.
#### Example Request
```bash Trigger a Business Event
curl --request POST \
--url 'https://api-01.moengage.com/v5/business-events/triggers' \
--header 'Authorization: Basic ' \
--header 'Content-Type: application/json' \
--header 'X-MOE-Request-Id: 550e8400-e29b-41d4-a716-446655440000' \
--data '{
"event_name": "price_drop",
"triggered_by": "ops@acme.com",
"event_attributes": {
"product_id": "SKU-99321",
"new_price": 349.5,
"dropped_at": "2026-08-03T09:12:00Z"
},
"moe_request_id": "550e8400-e29b-41d4-a716-446655440000"
}'
```
# Download Campaign Report
Source: https://moengage.com/docs/api/campaign-reports/download-campaign-report
/api/stats-report/stats-report.yaml get /campaign_reports/rest_api/{APP_ID}/{FILENAME}
This API downloads campaign reports for any specific date range. You can fetch reports for one-time and periodic campaigns.
#### Limits
* **Expiry**: The generated reports will expire in **7 days** from the date of creation.
* **Max Range**: You can generate reports for up to **90 days**.
#### Generating the Signature
A unique signature must be passed in the headers to verify the caller's authenticity.
The signature is `SHA256(Api_ID + "|" + FILENAME + "|" + SECRET_KEY)`.
```python
# SAMPLE IMPLEMENTATION IN PYTHON
from hashlib import sha256
Api_ID = "YOUR-APP-ID"
FILENAME = "Report_-_test_20210217.zip"
SECRET_KEY = "YOUR-SECRET-KEY"
Signature_Key = Api_ID + "|" + FILENAME + "|" + SECRET_KEY
# Now Signature is hexdigest of sha256 of Signature_Key
Signature = sha256(Signature_Key.encode('utf-8')).hexdigest()
```
# Audience and delivery reference
Source: https://moengage.com/docs/api/campaigns/audience-scheduling-delivery-reference
Reference for trigger_condition, segmentation_details, scheduling_details, delivery_controls, conversion_goal_details, control_group_details, utm_params, campaign_audience_limit, advanced, and geofences. Used by the Create Campaign and Update Campaign endpoints.
This reference covers the request-body components that define how a campaign reaches users: `trigger_condition`, `segmentation_details`, `scheduling_details`, `delivery_controls`, `conversion_goal_details`, `control_group_details`, `utm_params`, `campaign_audience_limit`, `advanced`, and `basic_details.geofences`. Used by [Create Campaign](/docs/api/create-campaigns/create-campaign-draft-v5) and [Update Campaign](/docs/api/update-campaigns/update-campaign-v5).
For `basic_details` (excluding geofences) and `campaign_content` per channel, platform, and template type, see [Campaign content reference](/docs/api/campaigns/campaign-content-reference).
The OpenAPI spec at `/api/campaigns/campaign-draft.yaml` is the authoritative source for field types, enums, and required markers. This page adds runnable variants and conditional rules not expressible in inline schema descriptions.
## Quick start
The minimum audience configuration is either `segmentation_details.is_all_user_campaign: true` or a single filter under `segmentation_details.included_filters`. The minimum schedule is `scheduling_details.delivery_type: ASAP`. Event-triggered campaigns also require `trigger_condition`.
```json One-time Push to a custom segment theme={null}
{
"segmentation_details": {
"included_filters": {
"filter_operator": "and",
"filters": [
{
"filter_type": "custom_segments",
"name": "High-LTV users",
"id": "seg_5f1a3b2c"
}
]
}
},
"scheduling_details": {
"delivery_type": "ASAP"
}
}
```
```json Event-triggered Email theme={null}
{
"trigger_condition": {
"included_filters": {
"filter_operator": "and",
"filters": [
{
"filter_type": "actions",
"action_name": "cart_abandoned",
"execution": { "type": "atleast", "count": 1 },
"executed": true
}
]
},
"trigger_delay_type": "ASAP"
},
"scheduling_details": {
"delivery_type": "AT_FIXED_TIME",
"start_time": "2026-07-15T09:00:00",
"expiry_time": "2026-12-31T23:59:59"
}
}
```
Everything below is reference material covering every supported delivery type, filter primitive, and delivery-control flag.
## Page contents
| Section | Location in request body |
| :-------------------------------------------------------------- | :-------------------------------------------------------------------------------------------------- |
| [Trigger conditions](#trigger-conditions) | `trigger_condition` |
| [Filter primitives](#filter-primitives) | `included_filters.filters[]`, `excluded_filters.filters[]`, `trigger_condition.*_filters.filters[]` |
| [Campaign audience](#campaign-audience) | `segmentation_details` |
| [Campaign delivery schedule](#campaign-delivery-schedule) | `scheduling_details` |
| [Delivery controls](#delivery-controls) | `delivery_controls` |
| [Conversion goal tracking](#conversion-goal-tracking) | `conversion_goal_details` |
| [Control groups](#control-groups) | `control_group_details` |
| [UTM parameters](#utm-parameters) | `utm_params` |
| [Campaign audience cap](#campaign-audience-cap) | `campaign_audience_limit` |
| [Advanced Push settings](#advanced-push-settings) | `advanced` (Push only) |
| [Geofence targeting](#geofence-targeting) | `basic_details.geofences` |
| [Validation rules](#validation-rules) | Cross-cutting rules enforced at validate time |
| [Updating an existing campaign](#updating-an-existing-campaign) | Per-state restrictions for `PATCH /v5/campaigns/{campaign_id}` |
## Trigger conditions
The `trigger_condition` object defines when a triggered campaign fires. It is **required** for the following delivery types:
* Push `EVENT_TRIGGERED`, `DEVICE_TRIGGERED`, and `LOCATION_TRIGGERED`.
* Email `EVENT_TRIGGERED`.
`BUSINESS_EVENT_TRIGGERED` campaigns identify the trigger via `basic_details.business_event` and do not use `trigger_condition`.
| Field | Type | Channel support |
| :------------------------------- | :-------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `included_filters` | [FilterGroup](#filter-primitives) | Push, Email. The primary condition that must match for the trigger to fire. |
| `secondary_included_filters` | [FilterGroup](#filter-primitives) | Push, Email. Additional filters combined with the primary condition. |
| `trigger_delay_type` | enum | Push: `DELAY`, `ASAP`, `INTELLIGENT_DELAY`. Email: `DELAY`, `ASAP`. |
| `trigger_delay_value` | integer | The numeric delay value. **Required** when `trigger_delay_type` is `DELAY`. |
| `trigger_delay_granularity` | enum | `MINUTES`, `HOURS`, or `DAYS`. **Required** when `trigger_delay_type` is `DELAY`. |
| `trigger_relation` | enum | `BEFORE` or `AFTER`. **Required** when `trigger_delay_type` is `DELAY`. |
| `trigger_attr` | object | The attribute used as the time anchor when `trigger_relation` is `BEFORE`. |
| `intelligent_delay_optimization` | object | Push only. **Required** when `trigger_delay_type` is `INTELLIGENT_DELAY`. See [Intelligent delay optimization (Push)](#intelligent-delay-optimization-push). |
`INTELLIGENT_DELAY` is supported on **Push only**. Email `trigger_delay_type` accepts `DELAY` or `ASAP`.
### Trigger delay variants
The campaign fires as soon as the trigger condition is met.
```json theme={null}
{
"trigger_condition": {
"included_filters": {
"filter_operator": "and",
"filters": [
{
"filter_type": "actions",
"action_name": "purchase_completed",
"execution": { "type": "atleast", "count": 1 },
"executed": true
}
]
},
"trigger_delay_type": "ASAP"
}
}
```
The campaign fires a fixed amount of time after the trigger condition is met.
```json theme={null}
{
"trigger_condition": {
"included_filters": {
"filter_operator": "and",
"filters": [
{
"filter_type": "actions",
"action_name": "cart_abandoned",
"execution": { "type": "atleast", "count": 1 },
"executed": true
}
]
},
"trigger_delay_type": "DELAY",
"trigger_delay_value": 30,
"trigger_delay_granularity": "MINUTES",
"trigger_relation": "AFTER"
}
}
```
`trigger_delay_value`, `trigger_delay_granularity`, and `trigger_relation` are all required when `trigger_delay_type` is `DELAY`.
The campaign fires before a time anchored to a user attribute (for example, a flight departure). `trigger_relation` is `BEFORE` and the time anchor is passed in `trigger_attr.name`.
```json theme={null}
{
"trigger_condition": {
"included_filters": {
"filter_operator": "and",
"filters": [
{
"filter_type": "actions",
"action_name": "ticket_booked",
"execution": { "type": "atleast", "count": 1 },
"executed": true
}
]
},
"trigger_delay_type": "DELAY",
"trigger_delay_value": 2,
"trigger_delay_granularity": "HOURS",
"trigger_relation": "BEFORE",
"trigger_attr": {
"name": "departure_time"
}
}
}
```
MoEngage picks the optimal send time per user within a min/max window. Push only.
```json theme={null}
{
"trigger_condition": {
"included_filters": {
"filter_operator": "and",
"filters": [
{
"filter_type": "actions",
"action_name": "session_start",
"execution": { "type": "atleast", "count": 1 },
"executed": true
}
]
},
"trigger_delay_type": "INTELLIGENT_DELAY",
"intelligent_delay_optimization": {
"min_delay_value": 1,
"min_delay_granularity": "HOURS",
"max_delay_value": 24,
"max_delay_granularity": "HOURS"
}
}
}
```
### Intelligent delay optimization (Push)
The `intelligent_delay_optimization` object defines the window within which MoEngage selects the optimal send time.
| Field | Type | Notes |
| :---------------------- | :------ | :---------------------------------------- |
| `min_delay_value` | integer | The numeric component of the lower bound. |
| `min_delay_granularity` | enum | `MINUTES` or `HOURS`. |
| `max_delay_value` | integer | The numeric component of the upper bound. |
| `max_delay_granularity` | enum | `HOURS` or `DAYS`. |
### Primary and secondary trigger filters
`secondary_included_filters` adds an extra filter group that must also match. Useful for triggers of the form "user did X and also Y".
```json theme={null}
{
"trigger_condition": {
"included_filters": {
"filter_operator": "and",
"filters": [
{
"filter_type": "actions",
"action_name": "purchase_completed",
"execution": { "type": "atleast", "count": 1 },
"executed": true
}
]
},
"secondary_included_filters": {
"filter_operator": "and",
"filters": [
{
"filter_type": "user_attributes",
"data_type": "string",
"name": "loyalty_tier",
"operator": "is",
"value": "gold"
}
]
},
"trigger_delay_type": "ASAP"
}
}
```
### Trigger requirements per delivery type
| Delivery type | `trigger_condition` | `basic_details.business_event` | `basic_details.geofences` |
| :--------------------------------------- | :------------------ | :----------------------------- | :----------------------------------------------------------- |
| `EVENT_TRIGGERED` (Push, Email) | **Required**. | — | — |
| `BUSINESS_EVENT_TRIGGERED` (Push, Email) | Not used. | **Required**. | — |
| `DEVICE_TRIGGERED` (Push) | **Required**. | — | — |
| `LOCATION_TRIGGERED` (Push) | **Required**. | — | **Required**. See [Geofence targeting](#geofence-targeting). |
| `ONE_TIME` (Push, Email) | Not applicable. | — | — |
| `PERIODIC` (Push, Email) | Not applicable. | — | — |
## Filter primitives
Filters are the primitives used inside `included_filters.filters[]` and `excluded_filters.filters[]` on `segmentation_details` and inside `included_filters.filters[]` and `secondary_included_filters.filters[]` on `trigger_condition`. Every filter group has the shape:
```json theme={null}
{
"filter_operator": "and",
"filters": [
/* one or more filter objects */
]
}
```
`filter_operator` is `and` or `or` (lowercase). Filters are objects discriminated by `filter_type`:
| `filter_type` | Purpose |
| :---------------- | :---------------------------------------------------------- |
| `user_attributes` | Match on a user attribute (string, double, datetime, bool). |
| `actions` | Match on whether a user performed an event. |
| `custom_segments` | Match users in a saved custom segment. |
```json theme={null}
{
"filter_type": "user_attributes",
"data_type": "string",
"category": "Tracked Standard Attribute",
"name": "country",
"operator": "is",
"value": "IN",
"case_sensitive": false,
"negate": false
}
```
| Field | Type | Notes |
| :--------------- | :------ | :-------------------------------------------------------------------------------------------- |
| `filter_type` | enum | `user_attributes` (fixed). |
| `data_type` | enum | `string`, `double`, `datetime`, or `bool` (lowercase). |
| `category` | string | The attribute category (for example, `Tracked Standard Attribute`). |
| `name` | string | The attribute name (for example, `country`, `uid`). |
| `operator` | string | The operator depends on `data_type`. See [Operators per data type](#operators-per-data-type). |
| `value` | varies | The value to match. Not required for the `exists` operator. |
| `case_sensitive` | boolean | Whether the comparison is case-sensitive. |
| `negate` | boolean | Whether to negate the condition. |
| `project_name` | string | **Required** when the Portfolio feature is enabled in the workspace. |
#### Operators per data type
| `data_type` | Allowed `operator` values |
| :---------- | :-------------------------------------------------------------------------------------------------------------- |
| `bool` | `is`, `exists` |
| `double` | `in`, `between`, `lessThan`, `greaterThan`, `exists` |
| `string` | `in`, `contains`, `containsInTheFollowing`, `startWithInTheFollowing`, `endsWithInTheFollowing`, `exists`, `is` |
| `datetime` | `inTheLast`, `on`, `between`, `before`, `after`, `inTheNext`, `exists`, `today` |
Filter on whether a user performed (or did not perform) an event.
```json theme={null}
{
"filter_type": "actions",
"action_name": "purchase_completed",
"execution": { "type": "atleast", "count": 1 },
"executed": true
}
```
| Field | Type | Notes |
| :---------------- | :-------------------------------- | :---------------------------------------------------------- |
| `filter_type` | enum | `actions` (fixed). |
| `action_name` | string | The event name. |
| `execution.type` | enum | `atleast`, `atmost`, or `exactly`. |
| `execution.count` | integer | The count compared against. |
| `executed` | boolean | Whether the action was executed. |
| `attributes` | [FilterGroup](#filter-primitives) | Optional. Filter on event attributes (nested filter group). |
| `condition` | string | Optional. Condition label (for example, `IF`). |
Action filter with event-attribute filters:
```json theme={null}
{
"filter_type": "actions",
"action_name": "product_viewed",
"execution": { "type": "atleast", "count": 1 },
"executed": true,
"attributes": {
"filter_operator": "and",
"filters": [
{
"filter_type": "user_attributes",
"data_type": "double",
"name": "price",
"operator": "greaterThan",
"value": 100
}
]
}
}
```
References a saved custom segment by name and ID.
```json theme={null}
{
"filter_type": "custom_segments",
"name": "High-LTV users",
"id": "seg_5f1a3b2c"
}
```
| Field | Type | Notes |
| :------------ | :----- | :-------------------------------------------------------- |
| `filter_type` | enum | `custom_segments` (fixed). |
| `name` | string | The custom segment name (as it appears in the dashboard). |
| `id` | string | The custom segment ID. |
The segment ID and name are visible in the MoEngage dashboard under **Segments**. For full segment construction details, refer to [Custom Segments](/docs/user-guide/segment/create-segments/manage-segments).
## Campaign audience
The `segmentation_details` object defines who receives the campaign. Two top-level modes are supported: an explicit filter group, or a flag that targets all users.
| Field | Type | Notes |
| :------------------------------- | :-------------------------------- | :-------------------------------------------------------------- |
| `included_filters` | [FilterGroup](#filter-primitives) | Filters that include users in the audience. |
| `excluded_filters` | [FilterGroup](#filter-primitives) | Filters that exclude users from the audience. |
| `is_all_user_campaign` | boolean | When `true`, all users are targeted (subject to opt-in status). |
| `send_campaign_to_opt_out_users` | boolean | When `true`, users who have opted out are also targeted. |
```json theme={null}
{
"segmentation_details": {
"is_all_user_campaign": true
}
}
```
```json theme={null}
{
"segmentation_details": {
"included_filters": {
"filter_operator": "and",
"filters": [
{
"filter_type": "custom_segments",
"name": "High-LTV users",
"id": "seg_5f1a3b2c"
}
]
}
}
}
```
```json theme={null}
{
"segmentation_details": {
"included_filters": {
"filter_operator": "and",
"filters": [
{
"filter_type": "user_attributes",
"data_type": "string",
"name": "country",
"operator": "is",
"value": "IN"
}
]
}
}
}
```
```json theme={null}
{
"segmentation_details": {
"included_filters": {
"filter_operator": "and",
"filters": [
{
"filter_type": "user_attributes",
"data_type": "string",
"name": "country",
"operator": "is",
"value": "IN"
}
]
},
"excluded_filters": {
"filter_operator": "and",
"filters": [
{
"filter_type": "custom_segments",
"name": "Internal testers",
"id": "seg_testers"
}
]
}
}
}
```
```json theme={null}
{
"segmentation_details": {
"is_all_user_campaign": true,
"send_campaign_to_opt_out_users": true
}
}
```
`send_campaign_to_opt_out_users: true` is intended for transactional or operational messaging where consent is implied by the user relationship.
## Campaign delivery schedule
The `scheduling_details.delivery_type` field selects the send model. Different fields are required based on the value.
| `delivery_type` | Send behavior | Required companion fields |
| :---------------------- | :-------------------------------------------------------- | :---------------------------------------- |
| `ASAP` | As soon as the campaign goes live. | None. |
| `AT_FIXED_TIME` | At a specific timestamp. | `start_time` (ISO 8601). |
| `SEND_IN_BTS` | At each user's optimal time within a window. | `start_time` and `bts_details`. |
| `SEND_IN_USER_TIMEZONE` | At a fixed wall-clock time in each user's local timezone. | `start_time` and `user_timezone_details`. |
For `PERIODIC` campaigns, `delivery_type` is `AT_FIXED_TIME` and `periodic_details` is required.
| Field | Type | Notes |
| :---------------------- | :------------------- | :------------------------------------------------------------------------------------------------- |
| `delivery_type` | enum | `ASAP`, `AT_FIXED_TIME`, `SEND_IN_BTS`, or `SEND_IN_USER_TIMEZONE`. |
| `start_time` | date-time (ISO 8601) | The campaign start time. |
| `expiry_time` | date-time (ISO 8601) | The campaign expiry time. Used on triggered campaigns to bound activity. |
| `periodic_details` | object | **Required** for `PERIODIC` campaigns. See [Periodic schedules](#periodic-schedules). |
| `bts_details` | object | **Required** when `delivery_type` is `SEND_IN_BTS`. See [Best Time to Send](#best-time-to-send). |
| `user_timezone_details` | object | **Required** when `delivery_type` is `SEND_IN_USER_TIMEZONE`. See [User timezone](#user-timezone). |
### Schedule variants
```json theme={null}
{
"scheduling_details": {
"delivery_type": "ASAP"
}
}
```
```json theme={null}
{
"scheduling_details": {
"delivery_type": "AT_FIXED_TIME",
"start_time": "2026-07-15T09:00:00",
"expiry_time": "2026-07-15T18:00:00"
}
}
```
`start_time` is in ISO 8601. `expiry_time` is optional and is commonly used on event-triggered campaigns to define a delivery window.
Best Time to Send: MoEngage selects the optimal send time per user based on historical engagement, within the window defined by `start_time` and `bts_details.window_end_time`.
```json theme={null}
{
"scheduling_details": {
"delivery_type": "SEND_IN_BTS",
"start_time": "2026-07-15T09:00:00",
"bts_details": {
"send_in_bts": true,
"if_user_bts_is_not_available": "send_at_start_time",
"if_user_bts_outside_time_window": "send_at_window_end",
"window_end_time": "6:43 am"
}
}
}
```
The campaign sends at the same wall-clock time in each user's local timezone.
```json theme={null}
{
"scheduling_details": {
"delivery_type": "SEND_IN_USER_TIMEZONE",
"start_time": "2026-07-15T09:00:00",
"user_timezone_details": {
"send_in_user_timezone": true,
"send_if_user_timezone_has_passed": false
}
}
}
```
When `send_if_user_timezone_has_passed` is `false`, users whose local time has already passed `start_time` are skipped.
Periodic campaigns combine `AT_FIXED_TIME` with `periodic_details`.
```json theme={null}
{
"scheduling_details": {
"delivery_type": "AT_FIXED_TIME",
"start_time": "2026-07-15T09:00:00",
"periodic_details": {
"sending_frequency": "WEEKLY",
"repeat_frequency": 1,
"repeat_on_days_of_week": ["MONDAY"]
}
}
}
```
### Periodic schedules
The `periodic_details` object is **required** for `PERIODIC` campaigns.
| Field | Type | Notes |
| :--------------------------------- | :------------------------------------------------------ | :---------------------------------------------------------------------------------------------------------------------------- |
| `sending_frequency` | enum | `DAILY`, `WEEKLY`, or `MONTHLY`. |
| `repeat_frequency` | integer | The repeat interval (for example, `1` for every week, `2` for every two weeks). |
| `no_of_occurences` | integer | The total number of times the campaign sends. |
| `repeat_on_date_of_month` | array of integer | Dates of the month to send on (1–31). Used with `MONTHLY`. |
| `repeat_on_days_of_week` | array of enum | One or more of `MONDAY`, `TUESDAY`, `WEDNESDAY`, `THURSDAY`, `FRIDAY`, `SATURDAY`, `SUNDAY`. Used with `WEEKLY` or `MONTHLY`. |
| `repeat_on_days_of_week_for_month` | array of `{ week_granularity, repeat_on_days_of_week }` | Used with `MONTHLY` to target specific weeks of the month. |
The `repeat_on_days_of_week_for_month` entries have the following shape:
| Field | Type | Notes |
| :----------------------- | :------------ | :----------------------------------------------- |
| `week_granularity` | enum | `FIRST`, `SECOND`, `THIRD`, `FOURTH`, or `LAST`. |
| `repeat_on_days_of_week` | array of enum | One or more day names. |
```json theme={null}
{
"scheduling_details": {
"delivery_type": "AT_FIXED_TIME",
"start_time": "2026-07-15T09:00:00",
"periodic_details": {
"sending_frequency": "DAILY",
"repeat_frequency": 1,
"no_of_occurences": 10
}
}
}
```
```json theme={null}
{
"scheduling_details": {
"delivery_type": "AT_FIXED_TIME",
"start_time": "2026-07-15T09:00:00",
"periodic_details": {
"sending_frequency": "WEEKLY",
"repeat_frequency": 1,
"repeat_on_days_of_week": ["FRIDAY"]
}
}
}
```
```json theme={null}
{
"scheduling_details": {
"delivery_type": "AT_FIXED_TIME",
"start_time": "2026-07-15T09:00:00",
"periodic_details": {
"sending_frequency": "MONTHLY",
"repeat_frequency": 1,
"repeat_on_date_of_month": [1, 15]
}
}
}
```
```json theme={null}
{
"scheduling_details": {
"delivery_type": "AT_FIXED_TIME",
"start_time": "2026-07-15T09:00:00",
"periodic_details": {
"sending_frequency": "MONTHLY",
"repeat_frequency": 1,
"repeat_on_days_of_week_for_month": [
{ "week_granularity": "FIRST", "repeat_on_days_of_week": ["MONDAY"] }
]
}
}
}
```
### Best Time to Send
The `bts_details` object is **required** when `delivery_type` is `SEND_IN_BTS`.
| Field | Type | Notes |
| :-------------------------------- | :------ | :------------------------------------------------------------------ |
| `send_in_bts` | boolean | Whether to send at the best time. |
| `if_user_bts_is_not_available` | string | Fallback behavior when a user's best time is unknown. |
| `if_user_bts_outside_time_window` | string | Fallback behavior when a user's best time falls outside the window. |
| `window_end_time` | string | The end of the BTS window. |
### User timezone
The `user_timezone_details` object is **required** when `delivery_type` is `SEND_IN_USER_TIMEZONE`.
| Field | Type | Notes |
| :--------------------------------- | :------ | :------------------------------------------------------------------------------- |
| `send_in_user_timezone` | boolean | Whether to send in each user's local timezone. |
| `send_if_user_timezone_has_passed` | boolean | Whether to still send to users whose local time has already passed `start_time`. |
## Delivery controls
The `delivery_controls` object carries throttling, frequency capping, DND behavior, and offline/queueing flags. The accepted fields differ between Push and Email.
### Push delivery controls
| Field | Type | Notes |
| :----------------------------------------------- | :------ | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `bypass_dnd` | boolean | Whether to bypass Do Not Disturb. **Required** for event-triggered campaigns. |
| `campaign_throttle_rpm` | integer | The throttle in requests per minute. **Not applicable** for device-triggered, location-triggered, and event-triggered campaigns. |
| `count_for_frequency_capping` | boolean | Whether sends from this campaign count toward workspace frequency caps. |
| `ignore_frequency_capping` | boolean | Whether this campaign bypasses workspace frequency caps. |
| `minimum_delay_between_two_notification_in_hour` | integer | Minimum hours between two pushes from this campaign. Applies to event-triggered and device-triggered campaigns. |
| `max_time_to_show_message_of_same_camapign` | string | Maximum hours the message is shown to a user. Applies to device-triggered campaigns. (Field name retains the spec typo `camapign`.) |
| `expiry_time_of_sync_data_in_hour` | string | Hours after which synced campaign data expires if the trigger condition is not met. Applies to device-triggered campaigns. |
| `send_message_in_offline_mode` | boolean | Whether to store and deliver the message while the device is offline. Applies to device-triggered campaigns. |
| `send_limit_value` | string | Maximum times a user can receive the campaign within the window. Applies to location-triggered campaigns. |
| `send_limit_granularity_in_hours` | string | The window in hours for `send_limit_value`. Applies to location-triggered campaigns. |
| `ignore_global_minimum_delay` | boolean | Whether to bypass the workspace-wide minimum interval between pushes. Applies to event-triggered campaigns. |
| `queuing_enabled` | boolean | Whether undeliverable messages are queued for later delivery. **Flag-gated**. Contact the MoEngage account team to enable. |
| `queue_duration` | integer | The hours to keep messages queued. Must be greater than `0` when `queuing_enabled` is `true`. **Flag-gated**. |
| `limit_send_config` | object | Configuration for limiting how many times a user can receive this campaign within a rolling time window. Applies to periodic and event-triggered campaigns. Not applicable to transactional campaigns. **Flag-gated**. Contact the MoEngage account team to enable. |
```json theme={null}
{
"delivery_controls": {
"campaign_throttle_rpm": 50000,
"count_for_frequency_capping": true,
"ignore_frequency_capping": false
}
}
```
```json theme={null}
{
"delivery_controls": {
"bypass_dnd": false,
"minimum_delay_between_two_notification_in_hour": 24,
"ignore_global_minimum_delay": false
}
}
```
```json theme={null}
{
"delivery_controls": {
"minimum_delay_between_two_notification_in_hour": 12,
"max_time_to_show_message_of_same_camapign": "48",
"expiry_time_of_sync_data_in_hour": "24",
"send_message_in_offline_mode": true
}
}
```
```json theme={null}
{
"delivery_controls": {
"send_limit_value": "1",
"send_limit_granularity_in_hours": "24"
}
}
```
```json theme={null}
{
"delivery_controls": {
"queuing_enabled": true,
"queue_duration": 12
}
}
```
`queuing_enabled` and `queue_duration` are flag-gated. Including them on a workspace where the feature is not enabled returns a validation error. For detailed behavior of queued messages, see [Message Queuing](/docs/user-guide/settings/channels/delivery-controls/message-queuing).
### Email delivery controls
| Field | Type | Notes |
| :----------------------------------------------- | :------ | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `bypass_dnd` | boolean | Whether to bypass Do Not Disturb. |
| `campaign_throttle_rpm` | integer | The throttle in requests per minute. |
| `count_for_frequency_capping` | boolean | Whether the campaign counts toward frequency caps. |
| `ignore_frequency_capping` | boolean | Whether to bypass frequency capping. |
| `minimum_delay_between_two_notification_in_hour` | integer | Minimum hours between two emails from this campaign. |
| `limit_send_config` | object | Configuration for limiting how many times a user can receive this campaign within a rolling time window. Applies to periodic and event-triggered campaigns. Not applicable to transactional campaigns.**Flag-gated**. Contact the MoEngage account team to enable. |
```json theme={null}
{
"delivery_controls": {
"campaign_throttle_rpm": 2000,
"count_for_frequency_capping": true,
"ignore_frequency_capping": false,
"minimum_delay_between_two_notification_in_hour": 24
}
}
```
## Conversion goal tracking
The `conversion_goal_details` object configures the events MoEngage tracks to attribute campaign success.
| Field | Type | Notes |
| :---------------------------- | :----------------------------------- | :------------------------------------------------------------------------ |
| `attribution_window_in_hours` | integer | The lookback window in hours for attributing goal events to the campaign. |
| `goals` | array of [Goal fields](#goal-fields) | The list of goals tracked. |
### Goal fields
| Field | Type | Notes |
| :--------------------- | :------ | :------------------------------------------------------------------------- |
| `goal_name` | string | The display name of the goal. |
| `goal_event_name` | string | The event tracked as a conversion. |
| `goal_event_attribute` | object | Optional. See [Goal event attribute fields](#goal-event-attribute-fields). |
| `is_primary_goal` | boolean | Whether the goal is the primary goal. |
| `revenue_attribute` | string | The event attribute used to track revenue. |
| `revenue_currency` | string | The currency for revenue tracking. |
#### Goal event attribute fields
| Field | Type | Notes |
| :------------------ | :-------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `name` | string | The attribute name. |
| `condition` | string | The condition (for example, `is`, `contains`, `between`). |
| `data_type` | enum | `STRING`, `DOUBLE`, `BOOL`, `NUMBER`, `GEOPOINT`, `DATETIME`, `ARRAY_DOUBLE`, `ARRAY_STRING`, `OBJECT`, or `ARRAY_OBJECT`. (Uppercase, differs from filter `data_type`.) |
| `value` | string | The match value. |
| `value1` | string | A secondary value (used with `between`). |
| `negate` | boolean | Whether to negate the condition. |
| `value_type` | string | The type of value being filtered. |
| `array_filter_type` | string | The logical filter type for array attributes. |
| `filters` | array of object | Sub-filters used when `data_type` is `OBJECT` or `ARRAY_OBJECT`. |
| `is_case_sensitive` | boolean | Whether the comparison is case-sensitive. |
```json theme={null}
{
"conversion_goal_details": {
"attribution_window_in_hours": 36,
"goals": [
{
"goal_name": "Purchase",
"goal_event_name": "purchase_completed",
"is_primary_goal": true,
"goal_event_attribute": {
"name": "category",
"condition": "is",
"data_type": "STRING",
"value": "electronics"
}
}
]
}
}
```
```json theme={null}
{
"conversion_goal_details": {
"attribution_window_in_hours": 72,
"goals": [
{ "goal_name": "Add to cart", "goal_event_name": "cart_added", "is_primary_goal": true },
{ "goal_name": "Purchase", "goal_event_name": "purchase_completed", "is_primary_goal": false }
]
}
}
```
## Control groups
The `control_group_details` object configures the campaign-level and global control groups (users held back from the send).
| Field | Type | Notes |
| :---------------------------------- | :-------------- | :--------------------------------------------------------------------------------------------------------- |
| `is_campaign_control_group_enabled` | boolean | Whether the campaign control group is enabled. |
| `campaign_control_group_percentage` | integer (0–100) | The percentage of the audience held back. **Required** when `is_campaign_control_group_enabled` is `true`. |
| `is_global_control_group_enabled` | boolean | Whether the workspace global control group applies to the campaign. |
```json theme={null}
{
"control_group_details": {
"is_campaign_control_group_enabled": true,
"campaign_control_group_percentage": 10,
"is_global_control_group_enabled": false
}
}
```
```json theme={null}
{
"control_group_details": {
"is_campaign_control_group_enabled": false,
"is_global_control_group_enabled": true
}
}
```
## UTM parameters
The `utm_params` object appends UTM tracking parameters to URLs in the campaign content. The five standard keys are explicitly defined. Up to **5 additional custom keys** prefixed with `utm_` are also supported.
| Field | Type | Notes |
| :------------------ | :----- | :----------------------------------------------------------------------------------- |
| `utm_source` | string | The source of the traffic. **Required** when UTM parameters are used. |
| `utm_medium` | string | The channel type. **Required** when UTM parameters are used. |
| `utm_campaign` | string | The campaign name. |
| `utm_term` | string | Search terms for paid traffic. |
| `utm_content` | string | The content element that differentiates links. |
| Custom `utm_*` keys | string | Up to 5 additional keys prefixed with `utm_` (for example, `utm_cust`, `utm_c1ust`). |
```json theme={null}
{
"utm_params": {
"utm_source": "google",
"utm_medium": "push",
"utm_campaign": "summer_sale",
"utm_term": "mobile+sale",
"utm_content": "banner",
"utm_cust": "value1",
"utm_c1ust": "value2"
}
}
```
The cap is 5 custom keys in total. Custom keys not prefixed with `utm_` are rejected.
## Campaign audience cap
The `campaign_audience_limit` object caps the number of users a campaign can reach.
**Flag-gated feature.** Not enabled by default for any workspace. Including `campaign_audience_limit` on a workspace where the flag is not enabled returns a `400` with `error.code: VALIDATION_FAILED` and `error.message: "Campaign Audience Limit feature is not enabled for this db"`. Contact the MoEngage account team to enable.
| Field | Type | Notes |
| :----------------------------------- | :------------------------ | :---------------------------------------------------------------------------------------------------------------------------------------------------- |
| `is_campaign_audience_limit_enabled` | boolean | Whether the cap is enforced. When `true`, `metric`, `frequency`, and `limit` are all required. When `false`, those three fields must not be provided. |
| `metric` | enum | `SENT` (the only value currently supported). **Required** when enabled. |
| `frequency` | enum | `TOTAL` (lifetime cap, all delivery types on Push and Email) or `INSTANCE` (per-send cap, **Periodic Push only**). **Required** when enabled. |
| `limit` | integer (1–9,999,999,999) | The maximum number of users. **Required** when enabled. |
**Supported channels:** Email, Push. Not supported for `BROADCAST_LIVE_ACTIVITY`.
```json theme={null}
{
"campaign_audience_limit": {
"is_campaign_audience_limit_enabled": true,
"metric": "SENT",
"frequency": "TOTAL",
"limit": 100000
}
}
```
Applies to all delivery types on both Push and Email.
```json theme={null}
{
"campaign_audience_limit": {
"is_campaign_audience_limit_enabled": true,
"metric": "SENT",
"frequency": "INSTANCE",
"limit": 50000
}
}
```
Supported only for **Periodic Push** campaigns. Caps the audience per send.
```json theme={null}
{
"campaign_audience_limit": {
"is_campaign_audience_limit_enabled": false
}
}
```
When `false`, `metric`, `frequency`, and `limit` must not be included.
## Advanced Push settings
The `advanced` object (Push only) carries notification-expiration settings and per-platform priority.
`advanced` is part of the Push request body. It is not part of the Email request body.
### Expiration settings
| Field | Type | Notes |
| :-------------------------------- | :------ | :--------------------------------------------- |
| `expire_notification_after_value` | integer | The numeric value for notification expiration. |
| `expire_notification_after_type` | enum | `HOUR` or `DAY`. |
| `remove_from_inbox_after_value` | integer | The numeric value for inbox removal. |
| `remove_from_inbox_after_type` | enum | `DAY` (the only accepted value). |
```json theme={null}
{
"advanced": {
"expiration_settings": {
"expire_notification_after_value": 24,
"expire_notification_after_type": "HOUR",
"remove_from_inbox_after_value": 7,
"remove_from_inbox_after_type": "DAY"
}
}
}
```
### Platform-level priority
| Field | Type | Notes |
| :--------------------------------------------- | :------ | :---------------------------------------------------- |
| `android_specific_priority.send_with_priority` | boolean | Whether to send with priority on Android. |
| `ios_specific_priority.apns_priority` | enum | `1`, `5`, or `10` (strings). The APNS priority. |
| `ios_specific_priority.interruption_level` | enum | `Passive`, `Active`, `Time sensitive`, or `Critical`. |
| `ios_specific_priority.relevance_score` | number | `0`, `0.5`, or `1`. |
```json theme={null}
{
"advanced": {
"platform_level_priority": {
"ios_specific_priority": {
"apns_priority": "10",
"interruption_level": "Time sensitive",
"relevance_score": 1
}
}
}
}
```
```json theme={null}
{
"advanced": {
"platform_level_priority": {
"android_specific_priority": {
"send_with_priority": true
}
}
}
}
```
## Geofence targeting
The `basic_details.geofences` object is **required** for Push `LOCATION_TRIGGERED` campaigns. The field lives structurally inside `basic_details` and is documented here because it works in concert with `trigger_condition` and `delivery_controls.send_limit_*` for location-triggered targeting.
| Field | Type | Notes |
| :-------------------------- | :----- | :----------------------------------------------------------------------------------------- |
| `name` | string | The unique name of the geofence. **Required**. |
| `latitude` | string | The center latitude. **Required**. |
| `longitude` | string | The center longitude. **Required**. |
| `radius` | string | The radius in meters. **Required**. |
| `dwell_time_value` | string | The numeric dwell time. **Required** when `triggered_at` is `dwell`. |
| `dwell_time_granularity` | enum | `MINUTES`, `HOURS`, or `DAYS`. **Required** when `triggered_at` is `dwell`. |
| `response_time_value` | string | The numeric response time before sending after the trigger condition is met. **Required**. |
| `response_time_granularity` | enum | `MINUTES`, `HOURS`, or `DAYS`. **Required**. |
| `triggered_at` | enum | `ENTRY`, `EXIT`, or `dwell` (note: `dwell` is lowercase). **Required**. |
The campaign fires when the user enters the geofence.
```json theme={null}
{
"basic_details": {
"platforms": ["ANDROID", "IOS"],
"geofences": {
"name": "Downtown Store",
"latitude": "40.758",
"longitude": "-73.985",
"radius": "500",
"response_time_value": "5",
"response_time_granularity": "MINUTES",
"triggered_at": "ENTRY"
}
}
}
```
The campaign fires when the user leaves the geofence.
```json theme={null}
{
"basic_details": {
"platforms": ["ANDROID", "IOS"],
"geofences": {
"name": "Downtown Store",
"latitude": "40.758",
"longitude": "-73.985",
"radius": "500",
"response_time_value": "5",
"response_time_granularity": "MINUTES",
"triggered_at": "EXIT"
}
}
}
```
The campaign fires after the user has remained inside the geofence for the configured dwell time.
```json theme={null}
{
"basic_details": {
"platforms": ["ANDROID"],
"geofences": {
"name": "Mall - Food Court",
"latitude": "40.758",
"longitude": "-73.985",
"radius": "200",
"dwell_time_value": "10",
"dwell_time_granularity": "MINUTES",
"response_time_value": "0",
"response_time_granularity": "MINUTES",
"triggered_at": "dwell"
}
}
}
```
The `triggered_at` value `dwell` is lowercase. The spec discriminates it from `ENTRY` and `EXIT` (both uppercase). When `dwell` is set, `dwell_time_value` and `dwell_time_granularity` are required.
## Validation rules
The following rules span multiple sub-objects on this page.
| Rule | Source |
| :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------ |
| `trigger_condition` is required for Push `EVENT_TRIGGERED`, `DEVICE_TRIGGERED`, `LOCATION_TRIGGERED`, and Email `EVENT_TRIGGERED`. | `PushTriggerCondition`, `EmailTriggerCondition` descriptions |
| `basic_details.business_event` is required for `BUSINESS_EVENT_TRIGGERED` (Push and Email). For `BUSINESS_EVENT_TRIGGERED`, `trigger_condition` is not used. | `PushBasicDetailsV5.business_event`, `EmailBasicDetailsV5.business_event` |
| `basic_details.geofences` is required for `LOCATION_TRIGGERED`. `LOCATION_TRIGGERED` is **Push only**. | [Geofence targeting](#geofence-targeting) |
| `geofences.triggered_at: dwell` requires `dwell_time_value` and `dwell_time_granularity`. The value `dwell` is **lowercase**. `ENTRY` and `EXIT` are uppercase. | `Geofences.triggered_at` |
| `trigger_delay_type: DELAY` requires `trigger_delay_value`, `trigger_delay_granularity`, and `trigger_relation`. | `PushTriggerCondition.trigger_delay_type`, `EmailTriggerCondition.trigger_delay_type` |
| `trigger_delay_type: INTELLIGENT_DELAY` is **Push only**. Requires `intelligent_delay_optimization` (with `min_delay_value`, `min_delay_granularity`, `max_delay_value`, `max_delay_granularity`). `min_delay_granularity` accepts `MINUTES` or `HOURS`. `max_delay_granularity` accepts `HOURS` or `DAYS`. | `PushTriggerCondition` description |
| `trigger_relation: BEFORE` requires `trigger_attr.name` (the time-attribute used as the anchor for the send). | `PushTriggerCondition.trigger_relation` |
| `filter_operator` is **lowercase**, `and` or `or`. | `FilterGroup.filter_operator` |
| `UserAttributeFilter.data_type` values are **lowercase** (`string`, `double`, `datetime`, `bool`). `GoalEventAttribute.data_type` values are **uppercase** (`STRING`, `DOUBLE`, ...). The schemas are distinct. | `UserAttributeFilter.data_type`, `GoalEventAttribute.data_type` |
| `UserAttributeFilter.operator` allowed values depend on `data_type`. Refer to [Operators per data type](#operators-per-data-type). | `UserAttributeFilter.operator` |
| `UserAttributeFilter.project_name` is **required** when the Portfolio feature is enabled in the workspace. | `UserAttributeFilter.project_name` |
| For `PERIODIC` campaigns, `scheduling_details.periodic_details` is required, and `scheduling_details.delivery_type` is `AT_FIXED_TIME`. | `PeriodicDetails` description |
| For `SEND_IN_BTS`, `bts_details` is required. For `SEND_IN_USER_TIMEZONE`, `user_timezone_details` is required. | `BTSDetails`, `UserTimezoneDetails` descriptions |
| `delivery_controls.bypass_dnd` is **required** for Push event-triggered campaigns. | `PushDeliveryControls.bypass_dnd` |
| `delivery_controls.campaign_throttle_rpm` is **not applicable** for Push device-triggered, location-triggered, and event-triggered campaigns. | `PushDeliveryControls.campaign_throttle_rpm` |
| `delivery_controls.queuing_enabled` and `queue_duration` are flag-gated. `queue_duration` must be greater than `0` when `queuing_enabled` is `true`. | `PushDeliveryControls.queuing_enabled` |
| `delivery_controls.send_limit_value` and `send_limit_granularity_in_hours` apply to **location-triggered** campaigns. | `PushDeliveryControls.send_limit_value` |
| `delivery_controls.max_time_to_show_message_of_same_camapign`, `expiry_time_of_sync_data_in_hour`, and `send_message_in_offline_mode` apply to **device-triggered** campaigns. | `PushDeliveryControls` |
| `control_group_details.campaign_control_group_percentage` is **required** when `is_campaign_control_group_enabled` is `true`. The percentage must be between `0` and `100`. | `ControlGroupDetails.campaign_control_group_percentage` |
| `campaign_audience_limit` is flag-gated. Including it on a workspace where the feature is not enabled returns a `400 VALIDATION_FAILED`. When enabled, `metric`, `frequency`, and `limit` are all required. When disabled, those three fields must not be provided. `frequency: INSTANCE` is **Periodic Push only**. | `CampaignAudienceLimit` description |
| `utm_params` accepts the 5 standard keys plus up to 5 additional `utm_`-prefixed custom keys. `utm_source` and `utm_medium` are required when UTM parameters are used. | `UTMParams` description |
| `advanced.expiration_settings.remove_from_inbox_after_type` accepts only `DAY`. Other granularities fail. | `AdvancedDetails.expiration_settings` |
| `advanced.platform_level_priority.ios_specific_priority.apns_priority` values are **strings** (`"1"`, `"5"`, `"10"`). `relevance_score` values are **numbers** (`0`, `0.5`, `1`). `interruption_level` accepts `Passive`, `Active`, `Time sensitive`, `Critical` (note the space and casing of `Time sensitive`). | `AdvancedDetails.platform_level_priority` |
## Updating an existing campaign
`PATCH /v5/campaigns/{campaign_id}` reuses every schema on this page. Additional rules apply for updates.
* When a field inside a nested object is updated, the **complete parent object** must be included in the request. For example, to change a single filter in `segmentation_details.included_filters.filters`, the full `segmentation_details` block is included.
* For campaigns in **`ACTIVE`** state, the following fields cannot be edited:
* `trigger_condition`
* `segmentation_details`
* `conversion_goal_details`
* The scheduling **type** (`delivery_type`)
* The scheduling **start date** (`scheduling_details.start_time`)
* For campaigns in **`SCHEDULED`** state, all fields except the scheduling type can be edited.
* Campaigns in **`STOPPED`** or **`ARCHIVED`** state cannot be updated.
* Updates to `trigger_condition` or `campaign_content` on event-triggered campaigns can take up to **30 minutes** to propagate to users due to content caching.
* For periodic campaigns, configuration changes apply from the next scheduled run.
* For one-time campaigns, changes apply to messages not yet dispatched at the time of the update.
```json theme={null}
{
"channel": "EMAIL",
"campaign_delivery_type": "{{campaign_delivery_type}}",
"updated_by": "{{user_email}}",
"segmentation_details": {
"included_filters": {
"filter_operator": "and",
"filters": [
{
"filter_type": "user_attributes",
"data_type": "string",
"name": "city",
"operator": "in",
"value": "{{city_value}}"
}
]
}
}
}
```
```json theme={null}
{
"channel": "PUSH",
"campaign_delivery_type": "{{campaign_delivery_type}}",
"updated_by": "{{user_email}}",
"delivery_controls": {
"bypass_dnd": false,
"campaign_throttle_rpm": 50000,
"count_for_frequency_capping": true
}
}
```
```json theme={null}
{
"channel": "EMAIL",
"campaign_delivery_type": "{{campaign_delivery_type}}",
"updated_by": "{{user_email}}",
"delivery_controls": {
"bypass_dnd": false,
"campaign_throttle_rpm": 2000,
"count_for_frequency_capping": true
}
}
```
```json theme={null}
{
"channel": "{{channel}}",
"campaign_delivery_type": "{{campaign_delivery_type}}",
"updated_by": "{{user_email}}",
"conversion_goal_details": {
"attribution_window_in_hours": 36,
"goals": [
{
"goal_name": "Goal 1",
"goal_event_name": "{{conversion_event_name}}",
"is_primary_goal": true
}
]
}
}
```
```json theme={null}
{
"channel": "{{channel}}",
"campaign_delivery_type": "{{campaign_delivery_type}}",
"updated_by": "{{user_email}}",
"control_group_details": {
"is_campaign_control_group_enabled": true,
"campaign_control_group_percentage": 10
}
}
```
```json theme={null}
{
"channel": "{{channel}}",
"campaign_delivery_type": "{{campaign_delivery_type}}",
"updated_by": "{{user_email}}",
"campaign_audience_limit": {
"is_campaign_audience_limit_enabled": true,
"metric": "SENT",
"frequency": "TOTAL",
"limit": 100000
}
}
```
```json theme={null}
{
"channel": "PUSH",
"campaign_delivery_type": "{{campaign_delivery_type}}",
"updated_by": "{{user_email}}",
"advanced": {
"expiration_settings": {
"expire_notification_after_value": 24,
"expire_notification_after_type": "HOUR"
},
"platform_level_priority": {
"ios_specific_priority": {
"apns_priority": "10",
"interruption_level": "Active"
},
"android_specific_priority": {
"send_with_priority": true
}
}
}
}
```
```json theme={null}
{
"channel": "PUSH",
"campaign_delivery_type": "LOCATION_TRIGGERED",
"updated_by": "{{user_email}}",
"basic_details": {
"geofences": {
"name": "{{geofence_name}}",
"latitude": "{{latitude}}",
"longitude": "{{longitude}}",
"radius": "{{radius_meters}}",
"response_time_value": "5",
"response_time_granularity": "MINUTES",
"triggered_at": "ENTRY"
}
}
}
```
## See also
* [Campaign content reference](/docs/api/campaigns/campaign-content-reference) — `basic_details` and `campaign_content` per channel, platform, and template type.
* [Create Campaign](/docs/api/create-campaigns/create-campaign-draft-v5) — required fields, happy-path cURLs, error responses.
* [Update Campaign](/docs/api/update-campaigns/update-campaign-v5) — per-state edit restrictions.
* [Update Campaign Status](/docs/api/update-campaigns/update-campaign-status-v5) — `STOP`, `PAUSE`, `RESUME` transitions.
* [Validate Campaign](/docs/api/create-campaigns/validate-campaign-v5) — publish-time validation check.
* [Campaign drafts overview](/docs/api/campaigns/campaign-draft-overview) — lifecycle, channels, supported delivery types.
# Campaign content reference
Source: https://moengage.com/docs/api/campaigns/campaign-content-reference
Reference for basic_details and campaign_content per channel, platform, and template type. Used by the Create Campaign and Update Campaign endpoints.
Use this reference to configure the request-body components that define campaign content for Push and Email campaigns. It covers `basic_details`, which carries identifying metadata, platform targeting, and platform-specific delivery flags, and `campaign_content`, which defines the message payload per channel, platform, and template type, including multi-locale and A/B variation support. Both components are used by [Create Campaign](/docs/api/create-campaigns/create-campaign-draft-v5) and [Update Campaign](/docs/api/update-campaigns/update-campaign-v5).
For audience targeting, scheduling, and delivery controls, see [Audience and delivery reference](/docs/api/campaigns/audience-scheduling-delivery-reference).
The OpenAPI spec at `/api/campaigns/campaign-draft.yaml` is the authoritative source for field types, enums, and required markers. This page adds runnable variants and conditional rules not expressible in inline schema descriptions.
## Quick start
The minimum content payload is a single channel-platform-template combination under `campaign_content.content.push` (Push) or an `html_content` or `custom_template_id` value under `campaign_content.content.email` (Email).
```json Push (Android BASIC) theme={null}
{
"campaign_content": {
"content": {
"push": {
"android": {
"template_type": "BASIC",
"basic_details": {
"notification_channel": "general",
"title": "Your order has shipped",
"message": "Tap to track it.",
"default_click_action": "DEEPLINKING",
"default_click_action_value": "https://example.com/orders"
}
}
}
}
}
}
```
```json Email (html_content) theme={null}
{
"campaign_content": {
"content": {
"email": {
"subject": "Your order has shipped",
"sender_name": "Example Team",
"from_address": "noreply@example.com",
"html_content": "
Hello {{UserAttribute['First Name']}}
"
}
}
}
}
```
Everything below this section is reference material covering every supported channel, platform, template type, and variation shape.
## Page contents
| Section | Location in request body |
| :-------------------------------------------------------------- | :------------------------------------------------------------- |
| [Push campaign metadata](#push-campaign-metadata) | `basic_details` on a Push request |
| [Email campaign metadata](#email-campaign-metadata) | `basic_details` on an Email request |
| [Content payload structure](#content-payload-structure) | `campaign_content.content` |
| [Android push content](#android-push-content) | `campaign_content.content.push.android` |
| [iOS push content](#ios-push-content) | `campaign_content.content.push.ios` |
| [Web push content](#web-push-content) | `campaign_content.content.push.web` |
| [Email content](#email-content) | `campaign_content.content.email` |
| [A/B test variations](#a%2Fb-test-variations) | `campaign_content.variation_details` |
| [Email delivery connector](#email-delivery-connector) | `connector` (Email requests only) |
| [Validation rules](#validation-rules) | Cross-cutting rules enforced at validate or publish |
| [Updating an existing campaign](#updating-an-existing-campaign) | Per-state restrictions for `PATCH /v5/campaigns/{campaign_id}` |
## Push campaign metadata
The `basic_details` object on a Push campaign carries identifying metadata, platform targeting, and platform-specific delivery flags. All fields are optional at creation. A subset becomes required based on `campaign_delivery_type`.
| Field | Type | Notes |
| :-------------------------------- | :-------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `name` | string | The campaign name shown in the dashboard. |
| `business_event` | string | The business event mapped to the campaign. **Required** when `campaign_delivery_type` is `BUSINESS_EVENT_TRIGGERED`. |
| `tags` | array of string | Free-form context tags. |
| `team` | string | The team collaborating on the campaign. See [Teams in MoEngage](https://help.moengage.com/hc/en-us/articles/360028586211-Teams-in-MoEngage). |
| `platforms` | array of string | Target platforms. Enum: `ANDROID`, `IOS`, `WEB`. |
| `broadcast_live_activity_id` | string | The broadcast Live Activity ID for iOS Live Activities. `BROADCAST_LIVE_ACTIVITY` is not supported through draft creation. Refer to [Validation rules](#validation-rules). |
| `geofences` | object | **Required** when `campaign_delivery_type` is `LOCATION_TRIGGERED`. The full schema is at [Geofence targeting](/docs/api/campaigns/audience-scheduling-delivery-reference#geofence-targeting). |
| `send_to_triggered_platform_only` | boolean | Applicable to event-triggered campaigns. When `true`, the campaign sends only to the platform that fired the trigger. |
| `platform_specific_details` | object | Platform-level delivery flags. See [Platform-specific delivery flags](#platform-specific-delivery-flags). |
### Platform-specific delivery flags
The `platform_specific_details` object carries per-platform delivery flags for Push campaigns.
A single Push Amp+ flag is defined for Android.
| Field | Type | Default | Notes |
| :---------------------- | :------ | :------ | :--------------------------------------------- |
| `push_amp_plus_enabled` | boolean | `false` | Whether Push Amp+ is enabled for the campaign. |
```json theme={null}
{
"basic_details": {
"platforms": ["ANDROID"],
"platform_specific_details": {
"android": {
"push_amp_plus_enabled": false
}
}
}
}
```
For iOS, exactly one of the three audience flags must be `true`. Passing none or more than one fails validation.
The three audience flags are mutually exclusive. Use the tab below to view each configuration.
```json All eligible devices theme={null}
{
"basic_details": {
"platforms": ["IOS"],
"platform_specific_details": {
"ios": {
"send_to_all_eligible_device": true,
"exclude_provisional_push_devices": false,
"send_to_only_provisional_push_enabled_devices": false
}
}
}
}
```
```json Exclude provisional theme={null}
{
"basic_details": {
"platforms": ["IOS"],
"platform_specific_details": {
"ios": {
"send_to_all_eligible_device": false,
"exclude_provisional_push_devices": true,
"send_to_only_provisional_push_enabled_devices": false
}
}
}
}
```
```json Provisional only theme={null}
{
"basic_details": {
"platforms": ["IOS"],
"platform_specific_details": {
"ios": {
"send_to_all_eligible_device": false,
"exclude_provisional_push_devices": false,
"send_to_only_provisional_push_enabled_devices": true
}
}
}
}
```
## Email campaign metadata
The `basic_details` object on an Email campaign carries identifying metadata, subscription category, and the recipient-email attribute.
| Field | Type | Notes |
| :-------------------------- | :-------------- | :------------------------------------------------------------------------------------------------------------------------------------------------ |
| `name` | string | The campaign name. |
| `business_event` | string | The business event mapped to the campaign. **Required** when `campaign_delivery_type` is `BUSINESS_EVENT_TRIGGERED`. |
| `content_type` | string | The type of content. Enum: `PROMOTIONAL`, `TRANSACTIONAL`. |
| `subscription_category` | string | The subscription category for promotional emails. **Required** when `content_type` is `PROMOTIONAL`. |
| `tags` | array of string | Free-form context tags. |
| `team` | string | The team collaborating on the campaign. |
| `user_attribute_identifier` | string | The user attribute that stores the recipient email address. Default: `Email (Standard)`. The internal identifier `MOE_EMAIL_ID` is also accepted. |
```json theme={null}
{
"basic_details": {
"name": "Summer Sale Email",
"content_type": "PROMOTIONAL",
"subscription_category": "music",
"tags": ["activation", "summer_sale"],
"team": "marketing_team",
"user_attribute_identifier": "Email (Standard)"
}
}
```
## Content payload structure
The `campaign_content.content` object accepts two shapes. The shape depends on whether locales or A/B test variations are configured on the campaign.
### Flat shape (no locales, no variations)
A flat object directly under `content`: `push` for Push campaigns, `email` for Email campaigns.
```json Push theme={null}
{
"campaign_content": {
"content": {
"push": {
"android": {
"template_type": "BASIC",
"basic_details": {
"title": "Your order has shipped",
"message": "Tap to track it.",
"notification_channel": "general",
"default_click_action": "DEEPLINKING",
"default_click_action_value": "https://example.com"
}
},
"ios": {
"template_type": "BASIC",
"basic_details": {
"title": "Your order has shipped",
"message": "Tap to track it.",
"default_click_action": "DEEPLINKING",
"default_click_action_value": "https://example.com"
}
}
}
}
}
}
```
```json Email theme={null}
{
"campaign_content": {
"content": {
"email": {
"subject": "Your order has shipped",
"sender_name": "Example Team",
"preview_text": "Track your delivery",
"from_address": "noreply@example.com",
"reply_to_address": "support@example.com",
"html_content": "
Hello {{UserAttribute['First Name']}}
"
}
}
}
}
```
### Locale-keyed and variation-keyed shape
When locales or A/B test variations are configured, `content` is keyed first by locale name and then by variation name. The shape is `content[locale_name][variation_name] = { push: { ... } }` for Push, and `content[locale_name][variation_name] = { email: { ... } }` for Email.
* The `"default"` locale key is always required. It serves as the fallback for users not matched to a named locale.
* Additional locale keys must each match a value listed in `campaign_content.locales` (for example, `"en-US"`, `"es-ES"`). The `"default"` locale is implicitly present and must not be listed in `campaign_content.locales`.
* The variation key (for example, `"variation_1"`) corresponds to the count in `variation_details.no_of_variations`. When no A/B test is configured, `"variation_1"` is used as the only key.
```json Component only theme={null}
{
"campaign_content": {
"locales": ["es-ES"],
"variation_details": {
"distribution_type": "MANUAL",
"no_of_variations": 2,
"manual_distribution_percentage": {
"variation_1": 50,
"variation_2": 50
}
},
"content": {
"default": {
"variation_1": { "push": { "android": { "template_type": "BASIC", "basic_details": { "title": "Summer Sale", "message": "Shop now" } } } },
"variation_2": { "push": { "android": { "template_type": "BASIC", "basic_details": { "title": "Big Discounts", "message": "Save more" } } } }
},
"es-ES": {
"variation_1": { "push": { "android": { "template_type": "BASIC", "basic_details": { "title": "Oferta de Verano", "message": "Compra ahora" } } } },
"variation_2": { "push": { "android": { "template_type": "BASIC", "basic_details": { "title": "Grandes Descuentos", "message": "Ahorra más" } } } }
}
}
}
}
```
```json Create request body (ONE_TIME, multi-locale A/B) theme={null}
{
"channel": "PUSH",
"campaign_delivery_type": "ONE_TIME",
"created_by": "{{user_email}}",
"basic_details": {
"name": "{{campaign_name}}",
"platforms": ["ANDROID"]
},
"campaign_content": {
"locales": ["es-ES"],
"variation_details": {
"distribution_type": "MANUAL",
"no_of_variations": 2,
"manual_distribution_percentage": {
"variation_1": 50,
"variation_2": 50
}
},
"content": {
"default": {
"variation_1": {
"push": {
"android": {
"template_type": "BASIC",
"basic_details": {
"title": "{{title_default_v1}}",
"message": "{{message_default_v1}}",
"default_click_action": "DEEPLINKING",
"default_click_action_value": "{{url}}"
}
}
}
},
"variation_2": {
"push": {
"android": {
"template_type": "BASIC",
"basic_details": {
"title": "{{title_default_v2}}",
"message": "{{message_default_v2}}",
"default_click_action": "DEEPLINKING",
"default_click_action_value": "{{url}}"
}
}
}
}
},
"es-ES": {
"variation_1": {
"push": {
"android": {
"template_type": "BASIC",
"basic_details": {
"title": "{{title_es_v1}}",
"message": "{{message_es_v1}}",
"default_click_action": "DEEPLINKING",
"default_click_action_value": "{{url}}"
}
}
}
},
"variation_2": {
"push": {
"android": {
"template_type": "BASIC",
"basic_details": {
"title": "{{title_es_v2}}",
"message": "{{message_es_v2}}",
"default_click_action": "DEEPLINKING",
"default_click_action_value": "{{url}}"
}
}
}
}
}
}
},
"scheduling_details": { "delivery_type": "ASAP" }
}
```
## Android push content
`campaign_content.content.push.android` (flat shape) or `content[locale][variation].push.android` (locale/variation shape). Accepted `template_type` values for Android:
`BASIC`, `STYLIZED_BASIC`, `SIMPLE_IMAGE_CAROUSEL`, `IMAGE_BANNER_WITH_TEXT`, `TIMER`, `TIMER_WITH_PROGRESS_BAR`, `Custom`.
`Custom` is mixed case (not `CUSTOM`). Submitting `CUSTOM` fails validation.
| Sub-object | Schema | When used |
| :------------------------ | :---------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------ |
| `basic_details` | [Android basic details fields](#android-basic-details-fields) | Always. Title, message, image, click action, and template-specific fields. |
| `timer` | [Android timer fields](#android-timer-fields) | **Required** for `TIMER` and `TIMER_WITH_PROGRESS_BAR`. |
| `buttons` | array of [Android button fields](#android-button-fields) | Optional. Action buttons. |
| `advanced` | [Android advanced fields](#android-advanced-fields) | Optional. TTL, sticky/dismiss behavior, group key. |
| `template_backup` | [Android template backup fields](#android-template-backup-fields) | **Required** for `STYLIZED_BASIC`, `SIMPLE_IMAGE_CAROUSEL`, `IMAGE_BANNER_WITH_TEXT`, `TIMER`, `TIMER_WITH_PROGRESS_BAR`. |
| `custom_template_id` | string | **Required** when `template_type` is `Custom`. |
| `custom_template_version` | integer | Optional. The custom template version. |
### Android template variants
The default template. Carries title, message, and an optional image or GIF.
```json theme={null}
{
"campaign_content": {
"content": {
"push": {
"android": {
"template_type": "BASIC",
"basic_details": {
"notification_channel": "general",
"title": "Limited Time Offer!",
"message": "Get 50% off on all items. Shop now!",
"default_click_action": "DEEPLINKING",
"default_click_action_value": "https://example.com/sale"
}
}
}
}
}
}
```
`input_gif_url` is supported on `BASIC` to render a GIF in place of a static image.
Same shape as `BASIC` with an added colored background, app-name color, and notification-control color.
```json theme={null}
{
"campaign_content": {
"content": {
"push": {
"android": {
"template_type": "STYLIZED_BASIC",
"basic_details": {
"title": "Limited Time Offer!",
"message": "Get 50% off on all items.",
"background_color_code": "#FFFFFF",
"app_name_color_code": "#dea1a1",
"notification_control_color": "LIGHT",
"image_url": "https://example.com/images/promo.jpg"
},
"template_backup": {
"title": "Limited Time Offer!",
"message": "Get 50% off on all items."
}
}
}
}
}
}
```
Supported `basic_details` fields specific to this template: `background_color_code`, `app_name_color_code`, `notification_control_color` (`LIGHT` or `DARK`), `apply_background_color_in_text_editor`. `template_backup` is required.
A scrollable image carousel. The slide list is carried in `carousel_content`.
```json theme={null}
{
"campaign_content": {
"content": {
"push": {
"android": {
"template_type": "SIMPLE_IMAGE_CAROUSEL",
"basic_details": {
"title": "New Collection",
"message": "Tap to browse.",
"image_scaling": "FIT_INSIDE_IMAGE_CONTAINER",
"carousel_content": {
"slider_transition": "manual",
"slide_data": [
{
"image_url": "https://example.com/slide1.jpg",
"image_click_action": "DEEPLINKING",
"image_click_action_value": "https://example.com/product/1"
},
{
"image_url": "https://example.com/slide2.jpg",
"image_click_action": "DEEPLINKING",
"image_click_action_value": "https://example.com/product/2"
}
]
}
},
"template_backup": {
"title": "New Collection",
"message": "Tap to browse."
}
}
}
}
}
}
```
For Android, `carousel_content.slider_transition` is lowercase (`manual` or `automatic`). For iOS, the same field is uppercase (`MANUAL` or `AUTOMATIC`). Refer to [iOS push content](#ios-push-content). `image_scaling` accepts `FIT_INSIDE_IMAGE_CONTAINER` or `FILL_IMAGE_CONTAINER`. `template_backup` is required.
A banner image with the title and message rendered on top.
```json theme={null}
{
"campaign_content": {
"content": {
"push": {
"android": {
"template_type": "IMAGE_BANNER_WITH_TEXT",
"basic_details": {
"title": "Flash Sale",
"message": "Today only — up to 70% off.",
"banner_image_url": "https://example.com/banner.jpg",
"include_title_and_message": true,
"include_app_name_and_time": false,
"collapsed_push_notification": "SAME_AS_TEMPLATE_BACKUP"
},
"template_backup": {
"title": "Flash Sale",
"message": "Today only — up to 70% off."
}
}
}
}
}
}
```
`banner_image_url` is required. `include_title_and_message` controls whether the title and message text is overlaid on the banner. `template_backup` is required.
A live countdown notification. `timer` is required.
```json theme={null}
{
"campaign_content": {
"content": {
"push": {
"android": {
"template_type": "TIMER",
"basic_details": {
"title": "Offer ends soon",
"message": "Hurry — limited time left."
},
"timer": {
"timer_ends_at": "DURATION",
"personalized_value": false,
"duration_hour": "2",
"duration_minute": "0"
},
"template_backup": {
"title": "Offer ends soon",
"message": "Limited time only."
}
}
}
}
}
}
```
The full `timer` schema is at [Android timer fields](#android-timer-fields). `template_backup` is required.
Same as `TIMER` with an added progress bar that visually depletes as time elapses.
```json theme={null}
{
"campaign_content": {
"content": {
"push": {
"android": {
"template_type": "TIMER_WITH_PROGRESS_BAR",
"basic_details": {
"title": "Order arriving",
"message": "Your delivery is on the way."
},
"timer": {
"timer_ends_at": "DURATION",
"personalized_value": false,
"duration_hour": "1",
"duration_minute": "30"
},
"template_backup": {
"title": "Order arriving",
"message": "Your delivery is on the way."
}
}
}
}
}
}
```
`timer` is required. `template_backup` is required.
References a Push template created in the MoEngage dashboard's Custom Template library.
```json theme={null}
{
"campaign_content": {
"content": {
"push": {
"android": {
"template_type": "Custom",
"custom_template_id": "tmpl_abc123",
"custom_template_version": 1
}
}
}
}
}
```
`custom_template_id` is required. `custom_template_version` is optional. When omitted, the latest published version is used.
### Android basic details fields
`AndroidBasicDetails` collects template-specific styling and click-action fields. Several fields apply only to specific templates.
| Field | Type | Supported templates |
| :-------------------------------------- | :------------------------ | :--------------------------------------------------------------------------------------------------------- |
| `notification_channel` | string | All. The Android notification channel where the push is delivered. |
| `title` | string | All. The notification title. |
| `message` | string | All. The body. HTML formatting is allowed. |
| `summary` | string | All. Summary text below the body. |
| `image_url` | URI | All (where supported by the template). |
| `input_gif_url` | URI | `BASIC`. A GIF rendered in the notification body. |
| `default_click_action` | enum | All. `DEEPLINKING`, `NAVIGATE_TO_A_SCREEN`, or `RICH_LANDING`. |
| `default_click_action_value` | string | All. The URL or deep link target for the click action. |
| `key_value_pairs` | array of `{ key, value }` | All. Custom payload keys forwarded to the SDK. |
| `background_color_code` | hex string | `STYLIZED_BASIC`, `SIMPLE_IMAGE_CAROUSEL`, `IMAGE_BANNER_WITH_TEXT`. |
| `app_name_color_code` | hex string | `STYLIZED_BASIC`, `SIMPLE_IMAGE_CAROUSEL`, `IMAGE_BANNER_WITH_TEXT`. |
| `notification_control_color` | enum | `STYLIZED_BASIC`, `SIMPLE_IMAGE_CAROUSEL`, `IMAGE_BANNER_WITH_TEXT`. `LIGHT` or `DARK`. |
| `apply_background_color_in_text_editor` | boolean | `STYLIZED_BASIC`, `SIMPLE_IMAGE_CAROUSEL`, `IMAGE_BANNER_WITH_TEXT`. |
| `include_app_name_and_time` | boolean | `IMAGE_BANNER_WITH_TEXT`. Renders the app name and timestamp on the banner. |
| `include_title_and_message` | boolean | `IMAGE_BANNER_WITH_TEXT`. Overlays the title and message on the banner. |
| `banner_image_url` | URI | **Required** for `IMAGE_BANNER_WITH_TEXT`. |
| `collapsed_push_notification` | string | `IMAGE_BANNER_WITH_TEXT`. Configuration for the collapsed view. |
| `image_scaling` | enum | `SIMPLE_IMAGE_CAROUSEL`, `IMAGE_BANNER_WITH_TEXT`. `FIT_INSIDE_IMAGE_CONTAINER` or `FILL_IMAGE_CONTAINER`. |
| `carousel_content` | object | **Required** for `SIMPLE_IMAGE_CAROUSEL`. See [Android carousel content](#android-carousel-content). |
#### Android carousel content
Configuration for the image carousel used by `SIMPLE_IMAGE_CAROUSEL` on Android.
| Field | Type | Notes |
| :-------------------------------------- | :------------------------ | :-------------------------------------------------------- |
| `slider_transition` | enum | `manual` or `automatic` (lowercase). |
| `slide_data[].image_url` | URI | The image for the slide. |
| `slide_data[].image_click_action` | enum | `DEEPLINKING`, `RICH_LANDING`, or `NAVIGATE_TO_A_SCREEN`. |
| `slide_data[].image_click_action_value` | string | **Required** when `image_click_action` is provided. |
| `slide_data[].key_value_pairs` | array of `{ key, value }` | Optional per-slide custom payload keys. |
### Android timer fields
`AndroidTimer` is required for `TIMER` and `TIMER_WITH_PROGRESS_BAR`.
| Field | Type | Notes |
| :------------------- | :-------- | :------------------------------------------------------------------------------------------------------- |
| `timer_ends_at` | enum | `DURATION`, `SPECIFIC_TIME_USER_TIMEZONE`, or `SPECIFIC_TIME_CAMPAIGN_TIMEZONE`. |
| `specific_time` | date-time | **Required** when `personalized_value` is `true`. |
| `time_period` | string | **Required** when `timer_ends_at` is `SPECIFIC_TIME_USER_TIMEZONE` or `SPECIFIC_TIME_CAMPAIGN_TIMEZONE`. |
| `personalized_value` | boolean | When `false`, the same duration applies to all users. |
| `duration_hour` | string | The number of hours the timer runs. **Required** when `personalized_value` is `false`. |
| `duration_minute` | string | The additional number of minutes the timer runs. **Required** when `personalized_value` is `false`. |
### Android button fields
Each entry in `buttons` is an `AndroidButton`.
| Field | Type | Notes |
| :------------------- | :------------------------ | :------------------------------------------------------------------------------------------------------------------------------------------- |
| `btn_name` | string | The visible button label. |
| `click_action_type` | enum | One of `DEEPLINKING`, `NAVIGATE_TO_A_SCREEN`, `RICH_LANDING`, `CALL`, `SHARE`, `COPY`, `SET_USER_ATTRIBUTE`, `TRACK_EVENT`, `CUSTOM_ACTION`. |
| `click_action_name` | string | The named action for `SET_USER_ATTRIBUTE`, `TRACK_EVENT`, or `CUSTOM_ACTION`. |
| `click_action_value` | string | The URL, deep link, attribute value, or event name (depending on `click_action_type`). |
| `key_value_pairs` | array of `{ key, value }` | Per-button custom payload keys. |
### Android advanced fields
`AndroidAdvanced` collects platform-level delivery flags.
| Field | Type | Notes |
| :------------------------------------------- | :------ | :----------------------------------------------------------------------------------------------------------------------------------------- |
| `coupon_code` | string | Coupon code carried in the payload. |
| `icon_type_in_notification` | string | The icon type label. |
| `use_large_icon` | boolean | Whether to use the large icon. |
| `make_notification_sticky` | boolean | When `true`, the user cannot swipe the notification away. |
| `dismiss_button_text` | string | **Required** when `make_notification_sticky` is `true` or `auto_dismiss_notification` is `true`. |
| `auto_dismiss_notification` | boolean | Whether the notification can be auto-dismissed. |
| `auto_dismiss_notification_time_value` | integer | **Required** when `auto_dismiss_notification` is `true`. |
| `auto_dismiss_notification_time_granularity` | enum | `DAYS`, `HOURS`, or `MINUTES`. **Required** when `auto_dismiss_notification` is `true`. |
| `group_key` | string | The group key for related notifications. MoEngage truncates to 45 characters and strips non-Latin scripts, special characters, and spaces. |
| `collapse_replace_key` | string | The update key for notifications that replace each other. |
### Android template backup fields
`AndroidTemplateBackup` defines the fallback notification rendered when the template cannot be displayed (for example, on older Android versions). Required for `STYLIZED_BASIC`, `SIMPLE_IMAGE_CAROUSEL`, `IMAGE_BANNER_WITH_TEXT`, `TIMER`, and `TIMER_WITH_PROGRESS_BAR`.
| Field | Type | Notes |
| :--------------------------- | :------------------------ | :-------------------------------------------------------- |
| `title` | string | Fallback title. |
| `message` | string | Fallback body. |
| `summary` | string | Fallback summary. |
| `image_url` | URI | Fallback image. |
| `default_click_action` | enum | `DEEPLINKING`, `NAVIGATE_TO_A_SCREEN`, or `RICH_LANDING`. |
| `default_click_action_value` | string | Fallback click action target. |
| `key_value_pairs` | array of `{ key, value }` | Per-fallback custom payload keys. |
## iOS push content
`campaign_content.content.push.ios`. Accepted `template_type` values for iOS:
`BASIC`, `STYLIZED_BASIC`, `SIMPLE_IMAGE_CAROUSEL`, `Custom`.
iOS does **not** support `IMAGE_BANNER_WITH_TEXT`, `TIMER`, or `TIMER_WITH_PROGRESS_BAR`. Submitting any of these for iOS fails validation.
| Sub-object | Schema | When used |
| :------------------------ | :-------------------------------------------------------- | :------------------------------------------------------------- |
| `basic_details` | [iOS basic details fields](#ios-basic-details-fields) | Always. |
| `buttons` | array of [iOS button fields](#ios-button-fields) | Optional. iOS button categories. |
| `advanced` | [iOS advanced fields](#ios-advanced-fields) | Optional. Custom sound, badge, group key. |
| `template_backup` | [iOS template backup fields](#ios-template-backup-fields) | **Required** for `STYLIZED_BASIC` and `SIMPLE_IMAGE_CAROUSEL`. |
| `custom_template_id` | string | **Required** when `template_type` is `Custom`. |
| `custom_template_version` | integer | Optional. |
### iOS template variants
```json theme={null}
{
"campaign_content": {
"content": {
"push": {
"ios": {
"template_type": "BASIC",
"basic_details": {
"title": "New Message",
"message": "You have a new message waiting for you",
"subtitle": "Inbox update",
"default_click_action": "DEEPLINKING",
"default_click_action_value": "https://example.com/inbox"
}
}
}
}
}
}
```
Optional rich media: `rich_media_type` (`Image`, `Video`, or `GIF`, in title case) with `rich_media_value` (the URL). `input_gif_url` is supported on `BASIC` and `STYLIZED_BASIC`.
```json theme={null}
{
"campaign_content": {
"content": {
"push": {
"ios": {
"template_type": "STYLIZED_BASIC",
"basic_details": {
"title": "Welcome back",
"message": "Pick up where you left off.",
"background_color_code": "#a0a0a0",
"image_url": "https://example.com/welcome.jpg"
},
"template_backup": {
"title": "Welcome back",
"message": "Pick up where you left off."
}
}
}
}
}
}
```
`background_color_code` and `apply_background_color_in_text_editor` are supported on this template. `template_backup` is required.
```json theme={null}
{
"campaign_content": {
"content": {
"push": {
"ios": {
"template_type": "SIMPLE_IMAGE_CAROUSEL",
"basic_details": {
"title": "New Arrivals",
"message": "Tap to swipe through.",
"image_url": "https://example.com/cover.jpg",
"carousel_content": {
"slider_transition": "AUTOMATIC",
"slide_data": [
{ "image_url": "https://example.com/slide1.jpg", "image_click_action": "DEEPLINKING", "image_click_action_value": "https://example.com/p/1" },
{ "image_url": "https://example.com/slide2.jpg", "image_click_action": "DEEPLINKING", "image_click_action_value": "https://example.com/p/2" }
]
}
},
"template_backup": {
"title": "New Arrivals",
"message": "Tap to swipe through."
}
}
}
}
}
}
```
`image_url` is required on iOS for `SIMPLE_IMAGE_CAROUSEL` (it serves as the cover image before the carousel loads). `carousel_content.slider_transition` for iOS is uppercase (`MANUAL` or `AUTOMATIC`). `template_backup` is required.
```json theme={null}
{
"campaign_content": {
"content": {
"push": {
"ios": {
"template_type": "Custom",
"custom_template_id": "tmpl_xyz789",
"custom_template_version": 1
}
}
}
}
}
```
`custom_template_id` is required.
### iOS basic details fields
| Field | Type | Supported templates |
| :-------------------------------------- | :------------------------ | :------------------------------------------------------------------------------------------- |
| `title` | string | All. |
| `message` | string | All. |
| `subtitle` | string | All. Rendered below the title. |
| `default_click_action` | enum | All. `DEEPLINKING`, `NAVIGATE_TO_A_SCREEN`, or `RICH_LANDING`. |
| `default_click_action_value` | string | All. |
| `key_value_pairs` | array of `{ key, value }` | All. |
| `allow_bg_refresh` | boolean | All. Whether the app can be woken in the background to refresh content. |
| `rich_media_type` | enum | `BASIC`. `Image`, `Video`, or `GIF` (title case). |
| `rich_media_value` | URI | `BASIC`. The media asset URL. |
| `input_gif_url` | URI | `BASIC`, `STYLIZED_BASIC`. A GIF URL. |
| `image_url` | URI | All. **Required** when `template_type` is `SIMPLE_IMAGE_CAROUSEL`. |
| `background_color_code` | hex string | `STYLIZED_BASIC`, `SIMPLE_IMAGE_CAROUSEL`. |
| `apply_background_color_in_text_editor` | boolean | `STYLIZED_BASIC`, `SIMPLE_IMAGE_CAROUSEL`. |
| `carousel_content` | object | **Required** for `SIMPLE_IMAGE_CAROUSEL`. See [iOS carousel content](#ios-carousel-content). |
#### iOS carousel content
| Field | Type | Notes |
| :-------------------------------------- | :------------------------ | :--------------------------------------------------------- |
| `slider_transition` | enum | `MANUAL` or `AUTOMATIC` (uppercase, differs from Android). |
| `slide_data[].image_url` | URI | The image for the slide. |
| `slide_data[].image_click_action` | enum | `DEEPLINKING`, `RICH_LANDING`, or `NAVIGATE_TO_A_SCREEN`. |
| `slide_data[].image_click_action_value` | string | The target for the slide click. |
| `slide_data[].key_value_pairs` | array of `{ key, value }` | Per-slide custom payload keys. |
### iOS button fields
iOS buttons use a category-based model. Buttons are pre-defined in the app, and the campaign references the category by name.
| Field | Type | Notes |
| :---------------- | :----- | :-------------------------------------------------------------------------------------- |
| `button_category` | string | The pre-defined category name configured in the app (for example, `MOE_PUSH_TEMPLATE`). |
### iOS advanced fields
| Field | Type | Notes |
| :--------------------- | :------ | :----------------------------------------------------------------------------------------------------------------------------------- |
| `coupon_code` | string | Coupon code carried in the payload. |
| `sound_file` | string | The name of a custom sound file in the app bundle. |
| `enable_ios_badge` | boolean | Whether the campaign increments the app's badge count. |
| `group_key` | string | The group key for related notifications. Truncated to 45 characters; non-Latin scripts, special characters, and spaces are stripped. |
| `collapse_replace_key` | string | The update key for notifications that replace each other. |
### iOS template backup fields
Required for `STYLIZED_BASIC` and `SIMPLE_IMAGE_CAROUSEL`.
| Field | Type | Notes |
| :--------------------------- | :------------------------ | :--------------------------------------------------------- |
| `title` | string | Fallback title. |
| `message` | string | Fallback body. |
| `subtitle` | string | Fallback subtitle. |
| `allow_bg_refresh` | boolean | Whether to enable background app refresh for the fallback. |
| `rich_media_type` | enum | `Image`, `Video`, or `GIF`. |
| `rich_media_value` | URI | The media URL. |
| `default_click_action` | enum | `DEEPLINKING`, `NAVIGATE_TO_A_SCREEN`, or `RICH_LANDING`. |
| `default_click_action_value` | string | The click target. |
| `key_value_pairs` | array of `{ key, value }` | Per-fallback custom payload keys. |
## Web push content
`campaign_content.content.push.web`. Web push currently supports only `template_type: BASIC`.
```json Component only theme={null}
{
"campaign_content": {
"content": {
"push": {
"web": {
"template_type": "BASIC",
"basic_details": {
"title": "Special Offer",
"message": "Check out our latest deals!",
"redirect_url": "https://example.com/offers",
"image_url": "https://example.com/hero.jpg",
"auto_dismiss_notification": false
},
"buttons": [
{ "title": "View Offer", "url": "https://example.com/offers", "icon_url": "https://example.com/icons/offer.png" }
],
"advanced": {
"icon_image_type": "ICON_URL",
"icon_url": "https://example.com/icon.png"
}
}
}
}
}
}
```
```json Create request body (ONE_TIME) theme={null}
{
"channel": "PUSH",
"campaign_delivery_type": "ONE_TIME",
"created_by": "{{user_email}}",
"basic_details": {
"name": "{{campaign_name}}",
"platforms": ["WEB"]
},
"campaign_content": {
"content": {
"push": {
"web": {
"template_type": "BASIC",
"basic_details": {
"title": "{{title}}",
"message": "{{message}}",
"redirect_url": "{{redirect_url}}",
"image_url": "{{image_url}}"
},
"buttons": [
{ "title": "{{button_label}}", "url": "{{button_url}}" }
]
}
}
}
},
"scheduling_details": { "delivery_type": "ASAP" }
}
```
### Web basic details fields
| Field | Type | Notes |
| :-------------------------- | :------ | :------------------------------------------------ |
| `title` | string | The notification title. |
| `message` | string | The notification body. |
| `redirect_url` | URI | The URL opened on click of the notification body. |
| `image_url` | URI | Optional large image. |
| `auto_dismiss_notification` | boolean | Whether the notification auto-dismisses. |
### Web button fields
Each entry in `buttons` is a `WebButton`.
| Field | Type | Notes |
| :--------- | :----- | :--------------------------------------------- |
| `title` | string | The button label. |
| `icon_url` | URI | Optional icon displayed next to the label. |
| `url` | URI | The destination opened on click of the button. |
### Web advanced fields
| Field | Type | Notes |
| :---------------- | :--- | :------------------------------------------------------------------ |
| `icon_image_type` | enum | `DEFAULT` or `ICON_URL`. |
| `icon_url` | URI | The custom icon URL. Required when `icon_image_type` is `ICON_URL`. |
## Email content
`campaign_content.content.email` carries the email message. Two mutually compatible content sources are supported: raw HTML in `html_content`, or a saved template referenced by `custom_template_id`. At least one of these must be present.
| Field | Type | Notes |
| :------------------------ | :---------------------------- | :------------------------------------------------------------------------------------------------------- |
| `subject` | string | The subject line. |
| `preview_text` | string | The preview text shown in inbox listings. |
| `sender_name` | string | The display name of the sender. |
| `from_address` | email | The sender email address. |
| `reply_to_address` | email | The reply-to address. |
| `cc_ids` | array of email | CC recipients. |
| `bcc_ids` | array of email | BCC recipients. |
| `html_content` | string | The raw HTML body. Optional when `custom_template_id` is provided. |
| `email_editor` | enum | `Froala Editor` or `Ace Editor`. Required when the campaign uses `Ace Editor`. |
| `custom_template_id` | string | A saved email template ID. When provided, `subject`, `preview_text`, and `sender_name` are not required. |
| `custom_template_version` | integer | Optional template version. |
| `attachments` | array of `{ file_type, url }` | See [Email attachments](#email-attachments). |
### Email content variants
```json theme={null}
{
"campaign_content": {
"content": {
"email": {
"subject": "Welcome to our store",
"sender_name": "Example Team",
"preview_text": "Get started in seconds",
"from_address": "hello@example.com",
"reply_to_address": "support@example.com",
"html_content": "
Hello {{UserAttribute['First Name']}}
"
}
}
}
}
```
```json theme={null}
{
"campaign_content": {
"content": {
"email": {
"custom_template_id": "email_tmpl_42",
"custom_template_version": 3
}
}
}
}
```
When `custom_template_id` is provided, `subject`, `preview_text`, and `sender_name` do not need to be repeated. The values are sourced from the saved template.
```json theme={null}
{
"campaign_content": {
"content": {
"email": {
"subject": "Release notes",
"from_address": "notify@example.com",
"html_content": "...",
"email_editor": "Ace Editor"
}
}
}
}
```
`email_editor: Ace Editor` is required when the campaign is composed using `Ace Editor`. The default `Froala Editor` does not require this field.
```json theme={null}
{
"campaign_content": {
"content": {
"email": {
"subject": "Order confirmation",
"from_address": "orders@example.com",
"html_content": "
Your order is confirmed.
",
"cc_ids": ["records@example.com"],
"bcc_ids": ["audit@example.com"]
}
}
}
}
```
### Email attachments
Each entry in `attachments` is `{ file_type, url }`.
| `file_type` | Behavior |
| :------------------------ | :----------------------------------------------------------------------------------------------- |
| `URL` | A static file hosted at a fixed URL. Every recipient receives the same file. |
| `PERSONALIZED_ATTACHMENT` | A personalized file generated per recipient via a URL that includes personalization expressions. |
```json URL attachment theme={null}
{
"campaign_content": {
"content": {
"email": {
"subject": "Latest brochure",
"from_address": "marketing@example.com",
"html_content": "
" } }
}
}
}
}
```
## Email delivery connector
The `connector` object is part of the Email request body (not the Email `campaign_content`) and identifies the delivery provider configured in the workspace.
`connector` is **required** on Email Create requests and Email inline test requests. It is not part of the Push request body.
| Field | Type | Notes |
| :--------------- | :----- | :---------------------------------------------------------- |
| `connector_type` | string | The connector service (for example, `SENDGRID`, `AWS SES`). |
| `connector_name` | string | The connector configuration name in the MoEngage workspace. |
```json theme={null}
{
"connector": {
"connector_type": "SENDGRID",
"connector_name": "Sendgrid1"
}
}
```
## Validation rules
The following rules span multiple sub-objects on this page. Each is enforced at validate or publish time.
| Rule | Source |
| :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :----------------------------------------------------------------------------------------- |
| For Push `template_type: Custom`, `custom_template_id` is required (Android, iOS). | `AndroidPushContent.custom_template_id`, `IOSPushContent.custom_template_id` |
| iOS supports only `BASIC`, `STYLIZED_BASIC`, `SIMPLE_IMAGE_CAROUSEL`, and `Custom`. Submitting `IMAGE_BANNER_WITH_TEXT`, `TIMER`, or `TIMER_WITH_PROGRESS_BAR` for iOS fails. | `IOSPushContent.template_type` enum |
| Web push supports only `BASIC`. | `WebPushContent.template_type` enum |
| For Android `STYLIZED_BASIC`, `SIMPLE_IMAGE_CAROUSEL`, `IMAGE_BANNER_WITH_TEXT`, `TIMER`, and `TIMER_WITH_PROGRESS_BAR`, `template_backup` is required. | `AndroidTemplateBackup` description |
| For iOS `STYLIZED_BASIC` and `SIMPLE_IMAGE_CAROUSEL`, `template_backup` is required. | `IOSTemplateBackup` description |
| For Android `IMAGE_BANNER_WITH_TEXT`, `banner_image_url` is required. | `AndroidBasicDetails.banner_image_url` |
| For iOS `SIMPLE_IMAGE_CAROUSEL`, `image_url` is required (used as the cover image before the carousel loads). | `IOSBasicDetails.image_url` |
| For Android `TIMER` and `TIMER_WITH_PROGRESS_BAR`, `timer` is required. When `personalized_value` is `false`, both `duration_hour` and `duration_minute` are required. When `personalized_value` is `true`, `specific_time` is required. | `AndroidTimer.duration_hour`, `AndroidTimer.duration_minute`, `AndroidTimer.specific_time` |
| For Android `STYLIZED_BASIC` and `SIMPLE_IMAGE_CAROUSEL`, the slider transition is **lowercase** (`manual` or `automatic`). For iOS `SIMPLE_IMAGE_CAROUSEL`, the slider transition is **uppercase** (`MANUAL` or `AUTOMATIC`). Mismatched casing fails validation. | `CarouselContent.slider_transition`, `IOSCarouselContent.slider_transition` |
| For Android, when `make_notification_sticky` is `true` or `auto_dismiss_notification` is `true`, `dismiss_button_text` is required. When `auto_dismiss_notification` is `true`, `auto_dismiss_notification_time_value` and `auto_dismiss_notification_time_granularity` are also required. | `AndroidAdvanced.dismiss_button_text` |
| For iOS, exactly one of `send_to_all_eligible_device`, `exclude_provisional_push_devices`, or `send_to_only_provisional_push_enabled_devices` must be `true` inside `basic_details.platform_specific_details.ios`. | `PlatformSpecificDetails.ios` description |
| For Email `content_type: PROMOTIONAL`, `subscription_category` is required. | `EmailBasicDetailsV5.subscription_category` |
| For Email, `html_content` is optional when `custom_template_id` is provided. When `custom_template_id` is provided, `subject`, `preview_text`, and `sender_name` are not required. | `EmailContent.html_content`, `EmailContent.custom_template_id` |
| For an A/B test with `distribution_type: MANUAL`, `manual_distribution_percentage` values must sum to 100. For `SHERPA`, `sherpa_campaign_duration` and `sherpa_distribution_metric` are both required. | `VariationDetails` description |
| For multi-locale or multi-variation campaigns, the `"default"` locale key is always required and serves as the fallback. Additional locale keys must match values listed in `campaign_content.locales`. | `PushCampaignContent.content`, `EmailCampaignContent.content` |
| `connector` is required on Email Create requests and Email inline test requests. | `Connector` description |
| `BROADCAST_LIVE_ACTIVITY` is **not** supported through draft creation. The V1 Campaigns API is used to send a Live Activity broadcast. | `PushCampaignCreateV5Request.campaign_delivery_type` description |
| `business_event` (inside `basic_details`) is required when `campaign_delivery_type` is `BUSINESS_EVENT_TRIGGERED`. | `PushBasicDetailsV5.business_event`, `EmailBasicDetailsV5.business_event` |
| `geofences` (inside `basic_details`) is required when `campaign_delivery_type` is `LOCATION_TRIGGERED`. The full schema is at [Geofence targeting](/docs/api/campaigns/audience-scheduling-delivery-reference#geofence-targeting). | `Geofences` description |
## Updating an existing campaign
`PATCH /v5/campaigns/{campaign_id}` reuses every schema on this page. Additional rules apply for updates.
* When a field inside a nested object is updated, the **complete parent object** must be included in the request. For example, to change only the title of an Android push, the full `campaign_content.content.push.android` block is included.
* The Update request body includes `updated_by` (the editing user's email, for audit purposes). When omitted, the update is attributed to the authenticated API credential.
* For campaigns in `ACTIVE` state, the following fields **cannot** be edited: `trigger_condition`, `segmentation_details`, `conversion_goal_details`, the scheduling type, and the scheduling start date. `campaign_content` and `basic_details.platforms` **can** be edited. The full per-state matrix is at [Update Campaign](/docs/api/update-campaigns/update-campaign-v5).
* Updates to `campaign_content` on event-triggered campaigns can take up to 30 minutes to propagate due to content caching.
* The Update Push schema accepts `BROADCAST_LIVE_ACTIVITY` as a `campaign_delivery_type` value, but draft creation does not. A draft cannot be transitioned to a Live Activity campaign through V5.
```json theme={null}
{
"channel": "PUSH",
"campaign_delivery_type": "{{campaign_delivery_type}}",
"updated_by": "{{user_email}}",
"campaign_content": {
"content": {
"push": {
"android": {
"template_type": "BASIC",
"basic_details": {
"title": "{{notification_title}}",
"message": "{{notification_message}}"
}
}
}
}
}
}
```
```json theme={null}
{
"channel": "EMAIL",
"campaign_delivery_type": "{{campaign_delivery_type}}",
"updated_by": "{{user_email}}",
"campaign_content": {
"content": {
"email": {
"subject": "{{email_subject}}",
"sender_name": "{{sender_name}}",
"from_address": "{{from_email}}",
"html_content": "{{html_body}}"
}
}
}
}
```
```json theme={null}
{
"channel": "PUSH",
"campaign_delivery_type": "{{campaign_delivery_type}}",
"updated_by": "{{user_email}}",
"basic_details": {
"platform_specific_details": {
"android": {
"push_amp_plus_enabled": true
},
"ios": {
"send_to_all_eligible_device": true
}
}
}
}
```
```json theme={null}
{
"channel": "PUSH",
"campaign_delivery_type": "{{campaign_delivery_type}}",
"updated_by": "{{user_email}}",
"control_group_details": {
"is_campaign_control_group_enabled": true,
"campaign_control_group_percentage": 10
}
}
```
```json theme={null}
{
"channel": "EMAIL",
"campaign_delivery_type": "{{campaign_delivery_type}}",
"updated_by": "{{user_email}}",
"control_group_details": {
"is_campaign_control_group_enabled": true,
"campaign_control_group_percentage": 10
}
}
```
## See also
* [Audience and delivery reference](/docs/api/campaigns/audience-scheduling-delivery-reference) — `trigger_condition`, `segmentation_details`, `scheduling_details`, `delivery_controls`, `conversion_goal_details`, `control_group_details`, `utm_params`, `campaign_audience_limit`, `advanced`, and `geofences`.
* [Create Campaign](/docs/api/create-campaigns/create-campaign-draft-v5) — required fields, happy-path cURLs, error responses.
* [Update Campaign](/docs/api/update-campaigns/update-campaign-v5) — per-state edit restrictions, publish action.
* [Update Campaign Status](/docs/api/update-campaigns/update-campaign-status-v5) — `STOP`, `PAUSE`, `RESUME` transitions.
* [Validate Campaign](/docs/api/create-campaigns/validate-campaign-v5) — pre-publish validation check.
* [Campaign drafts overview](/docs/api/campaigns/campaign-draft-overview) — lifecycle, channels, supported delivery types.
# Campaigns Overview
Source: https://moengage.com/docs/api/campaigns/campaign-draft-overview
Use the MoEngage Campaigns API to create, update, test, and manage campaigns programmatically. V5 supports campaign creation for Push and Email, with search, retrieval, and personalized preview available for SMS and other channels.
The Campaigns API (V5) lets you build campaigns incrementally rather than submitting a complete payload in a single request. Create a campaign in draft state, then add content, audience segments, trigger conditions, and scheduling across subsequent calls. Once the campaign is ready, validate its configuration, send test messages, and publish it.
Include an `Idempotency-Key` (UUID v4) on all POST and PATCH requests to ensure safe retries.
## Supported channels and delivery types
Channel support varies by operation:
| Operation | Supported channels |
| :-------------------------------------------------- | :------------------------------------------------------------------------- |
| Create Campaign, Update Campaign, Validate Campaign | `EMAIL`, `PUSH` (Android, iOS, Web) |
| Get Campaign, Search Campaigns, Get Campaign Meta | `EMAIL`, `PUSH`, `SMS`, `WHATSAPP`, `FACEBOOK`, `GOOGLE ADS`, `CONNECTORS` |
| Test Campaign | `EMAIL`, `PUSH` |
| Personalized Preview | `EMAIL`, `PUSH`, `SMS` |
SMS campaign creation and update are not supported in V5. Use the MoEngage dashboard or the V1 API to create and manage SMS campaigns. Existing SMS campaigns can be retrieved, searched, and previewed via V5.
**Supported delivery types:**
* `ONE_TIME`
* `PERIODIC`
* `EVENT_TRIGGERED`
* `BUSINESS_EVENT_TRIGGERED`
* `DEVICE_TRIGGERED` (Push only)
* `LOCATION_TRIGGERED` (Push only)
* `BROADCAST_LIVE_ACTIVITY` (Push iOS only)
## Campaign lifecycle
Start a draft with only the required fields: `channel`, `campaign_delivery_type`, and `created_by`. Add content, audience, and scheduling incrementally across subsequent update calls.
Patch individual components as you refine the setup. Each submitted component is validated in full before the draft is updated.
Check whether the draft would pass publish-time validation without committing any changes. This step is optional but recommended before testing or publishing.
Send a test message to specific users before going live. V5 supports two modes: **inline mode**, where you supply `channel` and `campaign_content` directly in the request without saving a draft, and **draft mode**, where you pass a `draft_id` to load content from a saved draft. Draft mode is new in V5.
Pause, resume, or stop a live campaign. Search your workspace and retrieve lightweight metadata across all campaigns.
If you are migrating from V1, here is what is new in V5:
* **Draft state:** Campaigns now start as drafts. Build the campaign incrementally, then validate and test before launching via V1.
* **Validate endpoint:** V5 adds a dedicated validate step to check your campaign configuration. There is no equivalent in V1.
* **Authentication header:** The `MOE-APPKEY` header (your Workspace ID) is optional, since Basic Auth already carries your Workspace ID as the username.
**Campaign publishing is not yet supported in V5.** To publish campaigns, use the V1 API (`PATCH /core-services/v1/campaigns/{campaign_id}`) in the interim.
## Campaign versioning
Campaign Versioning is **opt-in** per workspace. When it is turned on, publishing changes to a campaign that is already live creates a **new campaign document** with an incremented `version_number`. The **`campaign_id`** returned in API responses is the **canonical** identifier: it stays the same across versions so you can correlate drafts, search results, and analytics. Each version still has its own raw **`id`** (24-character ObjectId).
For UI-focused behavior and version history in the dashboard, see [Campaign versioning](/docs/user-guide/campaigns-and-channels/campaign-management-and-reports/campaign-versioning).
## Endpoints
The Campaigns API consists of the following endpoints to manage your campaign lifecycle:
* [Create Campaign](/docs/api/create-campaigns/create-campaign-draft-v5): Initializes a new Push or Email campaign in a `DRAFT` state using minimal required fields.
* [Get Campaign](/docs/api/get-campaign-details/get-campaign-v5): Retrieves a single campaign in its full deparsed form, displaying its current state and configuration.
* [Update Campaign](/docs/api/update-campaigns/update-campaign-v5): Updates specific components of an existing draft.
* [Validate Campaign](/docs/api/create-campaigns/validate-campaign-v5): Safely runs a full publish-time validation check without modifying the draft or changing its state.
* [Update Campaign Status](/docs/api/update-campaigns/update-campaign-status-v5): Applies lifecycle transitions (STOP, PAUSE, RESUME) to campaigns that have already been published.
* [Search Campaigns](/docs/api/get-campaign-details/search-campaigns): Searches for campaigns using granular filters, with the ability to explicitly include or exclude campaigns in draft state.
* [Get Campaign Meta](/docs/api/get-campaign-details/get-campaign-meta-v5): Fetches lightweight metadata, including daily cached reachability estimates for scheduled campaigns.
* [Test Campaign](/docs/api/test-campaigns/test-campaign): Sends a test push or email to up to 10 users. Supports **inline mode** (pass `channel` and `campaign_content` directly) and **draft mode** (pass `draft_id` to load content from a saved draft). Draft mode is new in V5.
* Personalized Preview: Returns a resolved preview of personalized campaign content for a specific user without sending a message. (Documentation temporarily unavailable while this endpoint is being revised.)
Get Child Campaigns and Update Global Control Group are not yet available in V5. Continue to use the V1 Campaigns API for these operations.
## Authentication
Authentication is done via Basic Auth. This requires a Base64-encoded string of your credentials in the format `username:password`.
* **Username**: Use your MoEngage Workspace ID (also known as the App ID). Find it in the MoEngage dashboard at **Settings** > **Account** > **API keys**.
* **Password**: Use an API key from **Settings** > **Account** > **API keys**.
Refer to [API Key Dashboard](/docs/user-guide/settings/account/api-and-api-keys/api-key-dashboard) for details on creating and managing API keys.
When creating an API key for this API, ensure the **Campaigns** checkbox is selected under **Select APIs for access**. The key permissions you select determine your access level:
* **View** for read endpoints
* **Create & Manage** to create and update campaigns
* **Create, Manage & Publish** to publish campaigns
## FAQs
### Create Campaign
Three fields are required at creation time: `channel` (PUSH or EMAIL), `campaign_delivery_type`, and `created_by` (the email of the user creating the campaign). All other components, including content, audience, scheduling, and delivery controls, are optional and can be added later using the Update Campaign endpoint.
Both approaches are supported. You can submit a minimal request with only the required fields and patch in the remaining components later, or you can include all sections in a single Create request. Any components included at creation time must meet the `DRAFT_CREATE` validation standards.
The `request_id` is an idempotency key scoped to campaign creation. For Push campaigns, the same `request_id` cannot be reused for one hour after a successful creation. For Email campaigns, the window is one day. If a creation attempt fails, you can retry immediately using the same `request_id`.
* **Push:** `ONE_TIME`, `PERIODIC`, `EVENT_TRIGGERED`, `BUSINESS_EVENT_TRIGGERED`, `DEVICE_TRIGGERED`, `LOCATION_TRIGGERED`, `BROADCAST_LIVE_ACTIVITY`
* **Email:** `ONE_TIME`, `PERIODIC`, `EVENT_TRIGGERED`, `BUSINESS_EVENT_TRIGGERED`
The API enforces two sets of limits:
* **Request rate:** 5 per second, 25 per minute, 100 per hour.
* **Campaign creation:** 5 successful per minute, 25 per hour, 100 per day.
Once 100 campaigns are successfully created within a day, subsequent requests are rejected regardless of the total number of API calls made.
MoEngage supports template expressions in content fields such as `title`, `message`, `subject`, and `html_content`. Use the following syntax to reference a user attribute at delivery time:
```
{{UserAttribute['First Name']}}
```
MoEngage also supports personalization using event attributes, content blocks, and the Content API. Refer to the MoEngage personalization documentation for the full syntax and configuration details for each source.
Campaigns created via the API are visible in the MoEngage dashboard under **Campaigns**, alongside campaigns created through the UI. Campaigns in `DRAFT` status can be updated via subsequent API calls.
No. `custom_template_id` and `html_content` are mutually exclusive in the Email campaign content payload. Submitting both fields in the same request returns a validation error. Use `custom_template_id` to reference a saved template from the MoEngage Email Template library, or use `html_content` to supply raw HTML directly.
Pass the segment reference inside `segmentation_details.included_filters` using `filter_type: custom_segments`. You can find the segment ID and name in the MoEngage dashboard under **Segments**.
```json theme={null}
{
"segmentation_details": {
"included_filters": {
"filter_operator": "and",
"filters": [
{
"filter_type": "custom_segments",
"name": "{{segment_name}}",
"id": "{{segment_id}}"
}
]
}
}
}
```
To exclude a segment, use the same structure under `excluded_filters`. You can combine multiple filter types, such as user attributes, actions, and custom segments, in the same `filters` array using the `filter_operator` (`and` / `or`).
### Get Campaign
Use the `campaign_id` path parameter, which is the 24-character ObjectId returned when you create or search for a campaign. Pass it in the `GET /v5/campaigns/{campaign_id}` request.
When campaign versioning is enabled, `campaign_id` is the stable canonical identifier that stays the same across all versions of a campaign. The `id` field is the raw ObjectId unique to each individual version document. Use `campaign_id` to correlate drafts, published campaigns, and analytics across versions.
The response reflects the campaign's current lifecycle state, which can be `DRAFT`, `SCHEDULED`, `ACTIVE`, `SENDING`, `PAUSED`, `SENT`, `STOPPED`, or `ARCHIVED`.
### Update Campaign
* **Active:** You cannot edit `trigger_condition`, `segmentation_details`, `conversion_goal_details`, the scheduling type, or the scheduling start date.
* **Scheduled:** All fields can be edited except the scheduling type.
* **Stopped / Archived:** No fields can be updated.
Updated content for Event-triggered campaigns is cached and can take up to 30 minutes to propagate to users.
No. Send only the components you want to update. However, if you are updating a field within a nested object, you must send the complete parent object. For example, to update the title of a push notification, include the full `campaign_content` object in the request body.
Component-level updates require the `campaigns:create_manage` scope.
No. The `segmentation_details` field cannot be updated for campaigns in `ACTIVE` state. To change the audience, stop the campaign and create a new one. See [Which fields cannot be edited once a campaign is Active?](#update-campaign) for the full list of non-editable fields by campaign state.
It depends on the delivery type:
* **Event-triggered campaigns:** Updated content is cached and can take up to 30 minutes to propagate to users after a successful update.
* **Periodic campaigns:** The updated configuration applies from the next scheduled run.
* **One-time campaigns:** Changes apply to any messages that have not yet been dispatched at the time of the update.
The `campaign_id` is returned in the `data.id` field of the Create Campaign response. You can also retrieve it using `GET /v5/campaigns/{campaign_id}` or by using the [Search Campaigns](/docs/api/get-campaign-details/search-campaigns-v5) endpoint to look up campaigns by name, status, channel, or delivery type.
Yes. The `template_type` field is part of `campaign_content`, which can be updated for campaigns in `ACTIVE` state. If you change the template type, include all required fields for the new template in the same request, as each template type has different required fields.
Yes. The `platforms` field in `basic_details` is not restricted for `ACTIVE` campaigns. Adding a platform extends delivery to that platform for future sends; removing a platform stops delivery to it.
### Validate Campaign
No. The validate endpoint performs a read-only publish-time validation check (`DRAFT_PUBLISH`) without modifying the campaign or changing its status.
The endpoint always returns HTTP `200`. Whether the campaign is valid or not is indicated in the response body through the `valid` field (`true` or `false`) and an `errors` array that lists any validation failures.
No. Unlike other POST and PATCH endpoints, the validate endpoint does not require an `Idempotency-Key` header.
### Update Campaign Status
The endpoint supports three actions. Each action applies only to specific delivery types and requires the campaign to be in a valid source state:
| Action | Supported delivery types | Valid source states |
| :------- | :---------------------------- | :----------------------------------------- |
| `STOP` | `ONE_TIME` | `ACTIVE`, `SCHEDULED`, `PAUSED`, `SENDING` |
| `PAUSE` | `PERIODIC`, `EVENT_TRIGGERED` | `ACTIVE`, `SCHEDULED`, `SENDING` |
| `RESUME` | `PERIODIC`, `EVENT_TRIGGERED` | `PAUSED` |
No. This endpoint only applies lifecycle transitions (STOP, PAUSE, RESUME) to campaigns that are already live. Campaign publishing is not yet supported in V5. To publish campaigns, use the V1 API in the interim.
`STOP` is not valid for Periodic or Event-triggered campaigns. Use `PAUSE` to temporarily halt a running Periodic or Event-triggered campaign and `RESUME` to restart it. Attempting to `STOP` a Periodic campaign returns a `422 Unprocessable Entity` error.
Yes. `STOP` is valid for One-time campaigns in `SCHEDULED`, `ACTIVE`, `PAUSED`, and `SENDING` states. If the campaign is in `SENDING` state, the action halts any remaining sends. Messages already dispatched before the stop is processed are still delivered.
* **Push:** Periodic and Event-triggered campaigns.
* **Email:** Periodic and Event-triggered campaigns.
* **Both channels:** Stopping a Scheduled One-time campaign.
### Search Campaigns
No. Campaigns in `DRAFT` state are excluded from results unless you explicitly include `DRAFT` in the `campaign_fields.status` array. This preserves backward compatibility with existing integrations that do not expect draft rows in results.
The maximum is 15 campaigns per page. Use the `limit` and `page` parameters to paginate through results.
Check the `flow_name` and `flow_id` fields in the response. If these fields are present, the campaign is a node within a flow.
When campaign versioning is enabled, each published revision appears as a separate document in search results, all sharing the same `campaign_id`. You can filter by `version_number` in the `campaign_fields` object to narrow results to a specific version.
Include `"SMS"` in the `campaign_fields.channels` array. Matching results return `channel: SMS` and include SMS-specific fields, `connector` (connector type and name) and `sender_name`, in each campaign object. Note that SMS campaign creation and update are not supported in V5; this endpoint retrieves existing SMS campaigns only.
Set `include_child_campaigns: true` in the request body. Flow-node campaigns are excluded from results by default. When this flag is enabled, flow-node campaigns appear in the results with `flow_id` and `flow_name` populated. Periodic child campaigns also appear with a `parent_id` field when this flag is set.
Set `include_archive_campaigns: true` in the request body. Archived campaigns are excluded by default, regardless of whether `ARCHIVED` is listed in `campaign_fields.status`. You must set this flag to `true` to include them.
There is no `sender_name` filter in the search request. To find SMS campaigns from a specific sender, pass `"SMS"` in `campaign_fields.channels` to retrieve all SMS campaigns, then filter by the `sender_name` field returned in each result on the client side.
### Get Campaign Meta
No. Reachability estimates are available only for scheduled campaigns: One-time, Business Event-triggered, and Event-triggered campaigns. The field is not populated for other campaign types.
No. Reachability is calculated once per day and cached for 24 hours. Multiple calls within the same day return the same cached value. The estimate may vary over time due to app installations, uninstalls, or changes in subscription status.
The endpoint supports Email, Push, SMS, WhatsApp, Facebook, Google Ads, and Connector-based campaigns.
If a campaign has been reviewed and rejected, the `rejection_comment` field appears in the meta response while the campaign remains in `DRAFT` status.
### Test Campaign
Yes. Use inline mode by including `channel` and `campaign_content` directly in the test request. The content is not stored on the server. For Email inline tests, also include the `connector` object. To test using a saved campaign, use draft mode and pass the `draft_id` instead.
You can send a test to a maximum of 10 users at a time using the `identifier_values` array.
Yes. In draft mode, the server sends one test per platform, locale, and variation by default. To narrow the send, specify `test_campaign_meta.platform` (ANDROID, IOS, or WEB), `locale_name`, or `variation` in the request.
Yes. Use `EMAIL` as the `identifier` type and provide the recipient's email address. The test will be sent, but the content will not be personalized with user profile data since there is no matching user record in MoEngage.
### Push Campaigns
Use the MoEngage Templates API to list Push templates available in your workspace. The response includes a `template_id` for each template. Pass this value as `custom_template_id` in the `campaign_content` payload when creating or updating a Push campaign.
Required fields vary by `template_type`. Set the `template_type` in the `basic_details` object of your campaign content payload. The supported values for Android are `BASIC`, `STYLIZED_BASIC`, `SIMPLE_IMAGE_CAROUSEL`, `IMAGE_BANNER_WITH_TEXT`, `TIMER`, `TIMER_WITH_PROGRESS_BAR`, and `Custom`. For iOS, the supported values are `BASIC`, `STYLIZED_BASIC`, `SIMPLE_IMAGE_CAROUSEL`, and `Custom`.
For the `Custom` template type, `custom_template_id` is required. For all other types, refer to the `campaign_content` schema in the [Create Campaign](/docs/api/create-campaigns/create-campaign-draft-v5) reference for the full list of required and optional fields per template type.
### SMS Campaigns
No. SMS campaign creation and update are not supported in V5. You can retrieve existing SMS campaigns using Get Campaign and Search Campaigns, and preview SMS content using Personalized Preview. To create and manage SMS campaigns, use the MoEngage dashboard or the V1 API.
The `sender_name` field in the campaign response carries the sender name configured for the campaign. This field is only populated for SMS campaigns. There is no `sender_name` filter in the Search Campaigns request. To find campaigns for a specific sender, retrieve all SMS campaigns and filter by `sender_name` on the client side.
The `connector` object in the campaign response contains the `connector_type` and `connector_name` for the campaign. For SMS campaigns, these fields identify the SMS delivery provider configured in your workspace.
### Personalized Preview
The endpoint supports `PUSH`, `EMAIL`, and `SMS`. Pass the target channel in the `channel` field of the request body.
No. The endpoint is read-only. It resolves all personalization expressions against the specified user's profile and returns the fully rendered content, but no message is delivered.
The endpoint resolves all standard MoEngage personalization sources: user attributes, event attributes, custom templates, content blocks, content APIs, and product sets. All Jinja expressions in the content are evaluated against the specified user's profile.
Pass the triggering event's attribute key-value pairs in the `event_attributes` object. The endpoint injects these values at resolution time to simulate how the content would render for a specific event. For example:
```json theme={null}
{
"event_attributes": {
"product_name": "Running Shoes",
"product_price": "4999"
}
}
```
In the content, reference these values using `{{ event.product_name }}`.
Pass `custom_template_id` in the `campaign_content` object, the same way you would in a Create Campaign request. The endpoint fetches and resolves the template, then returns the fully rendered output for the specified user.
No. The Personalized Preview endpoint only accepts inline content passed in `campaign_content`. It does not load content from a saved draft. To send a test message from a saved draft, use the Test Campaign endpoint with `draft_id`.
## Postman Collections
We have made it easy for you to test the APIs. Click [here](https://www.postman.com/moengage-dev/api-docs/collection/uhg5p67/moengage-campaign-apis) to view the Postman collection.
# Campaigns (Legacy) Overview
Source: https://moengage.com/docs/api/campaigns/campaigns-overview
Use the MoEngage Campaigns API to create, update, test, and manage Push and Email campaigns programmatically.
The MoEngage Campaigns API allows you to create and manage Push and Email campaigns. Use these endpoints to automate campaign creation, update existing campaigns, control campaign status, and retrieve campaign details.
If this API is not enabled for your account, contact your MoEngage Customer Success Manager (CSM) or the Support team to request enablement.
## Endpoints
The Campaigns API is a collection of the following endpoints:
* [Create Campaign](/docs/api/create-campaigns/create-campaign): Creates a new Push or Email campaign.
* [Update Campaign](/docs/api/update-campaigns/update-campaign-v1-—-legacy): Updates an existing Push or Email campaign in MoEngage.
* [Search Campaigns](/docs/api/get-campaign-details/search-campaigns): Fetches a list of Push, Email, or SMS campaigns with all current fields and status.
* [Test Campaign](/docs/api/test-campaigns/test-campaign): Sends a test Push or Email campaign to specific users before launching it.
* [Personalized Preview](/docs/api/test-campaigns/personalized-preview): Displays personalized content for a specific user before sending a Push, Email, or SMS campaign.
* [Get Campaign Meta](/docs/api/get-campaign-details/get-campaign-meta-v1-—-legacy): Retrieves campaign details and reachability information for scheduled campaigns.
* [Change Campaign Status](/docs/api/update-campaigns/change-campaign-status): Updates the status of campaigns to stop, pause, or resume them.
* [Get Child Campaigns](/docs/api/get-campaign-details/get-child-campaigns): Retrieves child campaign execution details for Periodic or Business Event-triggered campaigns.
## FAQs
### Create Email Campaign
You can use user attributes, event attributes, product sets, content API, and content block for personalizing campaigns. The syntax of the personalization Jinja is the same as supported in the MoEngage dashboard.
MoEngage dedups the campaign using the request ID. If the same request ID is passed twice in 24 hours, the second campaign creation will fail.
Campaigns are displayed under the same delivery type with which the campaigns are created through this API. For example, One-Time, Periodic, and so on.
No, only one of the two is required for creating the campaign. If both are passed, the campaign creation will display an error.
To create a campaign that uses a custom segment, include the `segmentation_details` in your campaign creation request. Within `included_filters`, set the `filter_type` to "custom\_segments" and provide the ID of your custom segment.
The following code snippet shows how to incorporate a custom segment:
```json theme={null}
"segmentation_details": {
"included_filters": {
"filter_operator": "and",
"filters": [
{
"filter_type": "custom_segments",
"name": "segment",
"id": "{{id}}"
}
]
}
}
```
### Create Push Campaign
Yes. You can create a Push campaign for a single platform such as Android, iOS, or Web. You can also create campaigns for a combination of these platforms.
Use the [Search Push Template API](/docs/api/templates/search-for-push-templates) and pass a request parameter such as the template name, created by, or any other supported parameter to retrieve the template ID in the response.
Select a template type, such as Stylized Basic, Timer, or any other available option. Then, refer to the template's requirements and include all fields marked as mandatory for that template type.
To create a campaign that uses a custom segment, include the `segmentation_details` in your campaign creation request. Within `included_filters`, set the `filter_type` to "custom\_segments" and provide the ID of your custom segment.
The following code snippet shows how to incorporate a custom segment:
```json theme={null}
"segmentation_details": {
"included_filters": {
"filter_operator": "and",
"filters": [
{
"filter_type": "custom_segments",
"name": "segment",
"id": "{{id}}"
}
]
}
}
```
### Update Push Campaign
The template type can be modified for the required platforms, and new content needs to be provided for the updated template type.
The platform can be added or removed during the update of the campaigns. You need to provide content for the new platform added to the campaign.
The updated campaign starts sending out as soon as changes are done, except for Event-triggered campaigns. In Event-triggered campaigns, details are cached and take up to 30 mins to send the updated campaign.
### Update Email Campaign
No, you cannot update the segmentation audience after the campaign is in Active state. You can update the segmentation audience if the campaign is in Scheduled state or create a duplicate by stopping the existing campaign to pass new segmentation details.
The updated campaign starts sending out as soon as changes are done except for Event-triggered campaigns. In Event-triggered campaigns, details are cached and take up to 30 mins to send the updated campaign.
You must call the "GET" API by the campaign name, channel, or any other filters to fetch the campaign ID of the desired campaign. For more information, refer to [Search Campaigns](/docs/api/get-campaign-details/search-campaigns).
### Search Campaigns - Push
Yes. To fetch flow node campaigns, you must pass the `include_child_campaigns` key value as `true`.
Use the `flow_name` and `flow_id` keys in the response. These keys indicate that the campaign is part of a flow.
The response will include all flow node campaigns, regardless of the flow delivery type, and Engage campaigns with a delivery type of `one_time`.
### Search Campaigns - SMS
Yes. To fetch flow node campaigns, you must pass the `include_child_campaigns` key value as `true`.
Use the `flow_name` and `flow_id` keys in the response. These keys indicate that the campaign is part of a flow.
No, we currently do not have any filter in the request body for the sender name.
Yes. To fetch archived campaigns, you must pass the `include_archived_campaigns` key value as `true`.
### Test Push Campaign
Yes, you can send the test campaign to any user by passing identifier as "Email". The campaign sent will not be personalized.
The values passed under personalization details will be used for all users to whom the test campaign is to be sent and the remaining user attributes values will be picked up from the user profile.
### Test Email Campaign
Yes, you can send the test campaign to any user by passing identifier as "Email". The campaign sent will not be personalized.
The values passed under personalization details will be used for all users to whom the test campaign is to be sent and the remaining user attributes values will be picked up from the user profile.
### Get Campaign Meta
No, reachability is an estimated value that may vary over time due to factors such as app installations, uninstalls, or changes in email subscription status.
No, reachability is calculated once daily and cached for 24 hours. Multiple API calls within the same day will return the cached value.
### Personalized Preview
Yes, you can use custom templates with the Personalization Preview. The API will personalize the content within the template based on the user identifier provided and return the personalized HTML as a response.
Yes, you can pass event details as input to the API. Ensure the event name matches the one in your workspace.
### Change Campaign Status
Periodic campaign only supports Pause and Resume actions of the campaign. You can stop only One-time campaigns.
No, after the One-time campaign moved to the Active state, you cannot stop it.
## Postman Collections
We have made it easy for you to test the APIs. Click [here](https://www.postman.com/moengage-dev/workspace/api-docs/folder/3182294-c560e43e-4e5e-4d1c-a100-f5f5070780b1?action=share\&creator=3182294\&ctx=documentation) to view the Postman collection.
# V1 vs. V5 Search Campaigns: Behavioral Differences
Source: https://moengage.com/docs/api/campaigns/search-campaigns-v5-migration
A comparison of behavioral, structural, and field-level differences between the V1 and V5 Search Campaigns APIs to help you understand what has changed.
The V5 Search Campaigns API introduces meaningful improvements in how campaigns are retrieved and filtered. This page covers the behavioral differences between V1 and V5 to help you understand what has changed and why.
For the full V5 Search Campaigns API reference, see [POST /v5/campaigns/search](/docs/api/get-campaign-details/search-campaigns-v5).
## Endpoint structure
In V1, a single endpoint — `POST /v1/campaigns/search` — handled both single-campaign retrieval by ID and multi-campaign filtering. V5 separates these into two distinct endpoints to make each operation explicit:
| Use case | V1 endpoint | V5 endpoint |
| :------------------------------------------ | :------------------------------------------------------ | :-------------------------------- |
| Retrieve a single campaign by ID | `POST /v1/campaigns/search` (with `campaign_fields.id`) | `GET /v5/campaigns/{campaign_id}` |
| Search and filter across multiple campaigns | `POST /v1/campaigns/search` | `POST /v5/campaigns/search` |
## Request field changes
Two filter keys under `campaign_fields` were renamed between V1 and V5. All other V1 filter fields — `channels`, `created_by`, `created_date`, `name`, `status`, and `tags` — are unchanged.
| V1 field | V5 field | What changed |
| :------------------------------ | :--------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------ |
| `campaign_fields.id` (string) | `campaign_fields.ids` (array of strings) | Renamed and type changed from string to array. To replicate V1 single-ID behavior, pass a single-element array: `"ids": [""]`. |
| `campaign_fields.delivery_type` | `campaign_fields.campaign_delivery_type` | Renamed. Sending the V1 key name returns zero results with no error. |
## `request_id` is now optional
In V1, `request_id` was required on every search request. In V5, it is optional. Providing it is still recommended for traceability — MoEngage Support can use it to locate specific requests in server logs when troubleshooting.
## Response field changes
The campaign identifier field was renamed. V1 returned it as `campaign_id`; V5 returns it as `id`.
## Draft visibility
In V5, draft campaigns are excluded from search results by default. Drafts appear only when `DRAFT` is explicitly included in `campaign_fields.status`. In V1, this behavior was not enforced the same way, so you may notice fewer results in V5 if your integration does not include `DRAFT` in status filters.
## Campaign versioning
When campaign versioning is enabled in V5, responses include a `version_number` field and you can filter by it. Each published revision is a separate document linked by the same canonical `campaign_id`. V1 did not support versioning.
## Pagination behavior
V5 search results are paginated up to 15 campaigns per page. Unlike V1, V5 responses do not include a `total_count` field. To determine the total number of pages, continue paginating until a page returns fewer results than the value specified in `limit`.
## SMS campaign support
SMS campaigns are included in V5 search results when `"SMS"` is passed in the `channels` array. The `channel` field in each matching result returns `SMS`. Full SMS campaign creation and update operations are not supported in V5.
## Child campaign executions
To retrieve the execution history of Periodic and Business Event-triggered campaigns — including child campaign IDs, sent times, statuses, and total instance count — use `POST /v5/campaigns/{parent_campaign_id}/executions`. This is the V5 equivalent of `POST /v1/campaigns/{parent_id}/executions`.
# Cards Overview
Source: https://moengage.com/docs/api/cards/cards-overview
Use the MoEngage Cards API to fetch, filter, and delete user-specific cards from your application.
The MoEngage Cards API allows you to fetch and delete user-specific cards from the MoEngage database. Use these endpoints to retrieve cards for a user, filter them by platform and category, check for updates since the last sync, and remove specific card campaigns.
If this API is not enabled for your account, contact your MoEngage Customer Success Manager (CSM) or the Support team to request enablement.
## Endpoints
The Cards API is a collection of the following endpoints:
* [Fetch Cards](/docs/api/cards/fetch-cards-for-user): Retrieves all active and updated cards for a specific user, supporting filtering and pagination.
* [Delete Cards](/docs/api/cards/delete-cards-for-user): Deletes specific card campaigns for a user from the MoEngage Cards database.
## FAQs
### Fetch Cards
You can use the `prev_sync_card_ids` (list of card IDs from the previous sync) and `last_updated_time` (Unix epoch timestamp of the last sync) fields in the request body. The API will return only the new or updated cards.
Yes. You can filter by platform using the `platforms` array (e.g., `["ANDROID", "WEB"]`) and by category using the `card_category` string field in the request body.
You must provide either the `uid` (MoEngage Standard ID like Email or Mobile Number) or the `unique_id` (Platform-specific device identifier). One of these is required.
The *unique\_id* is the unique value that identifies the user to whom the cards need to be shown. It is a platform-specific device identifier that can be generated.
The *uid* is the unique MoEngage Standard ID (Email ID Standard or the Mobile Number Standard) that identifies the user.
### Delete Cards
The Delete Cards API requires complex filtering parameters (campaign IDs, specific platforms, and user identifiers) which are best transmitted via a JSON body, even though this deviates from the standard REST pattern for `DELETE` requests.
Yes, you can specify the `platforms` array in the request body (e.g., `["android"]`) to delete the cards only from those specific platforms. If omitted, it deletes from all associated platforms.
The *unique\_id* is the unique value that identifies the user to whom the cards need to be deleted. It is a platform-specific device identifier that can be generated. The *uid* is the unique MoEngage Standard ID that identifies the user.
## Postman Collections
We have made it easy for you to test the APIs. Click [here](https://www.postman.com/moengage-dev/api-docs/collection/dpuqrlz/moengage-cards) to view the Postman collection.
# Delete Cards for User
Source: https://moengage.com/docs/api/cards/delete-cards-for-user
/api/cards/cards.yaml delete /cards/delete
This API deletes cards for a specified user from the MoEngage Cards database.
#### Rate Limit
The rate limit is **50K RPM** (Requests Per Minute) and is applicable at the workspace (App) level.
# Fetch Cards for User
Source: https://moengage.com/docs/api/cards/fetch-cards-for-user
/api/cards/cards.yaml post /cards/fetch
This API retrieves all active and updated cards for a specific user from the MoEngage Cards database. You can filter the search results based on the platform and card category and check if any card was updated for the user since the previous sync.
#### Rate Limit
The rate limit is **50K RPM** (Requests Per Minute) and is applicable at the workspace (App) level.
# Add Catalog Attributes
Source: https://moengage.com/docs/api/catalog/add-catalog-attributes
/api/catalog/catalog.yaml patch /catalog/{catalog_id}/attributes
This API adds new attributes to the catalog. If the API request contains attributes that already exist, they will not be added again.
#### Rate Limit
* Request limit: You can add 100 attributes per minute OR 1000 attributes per hour.
* Payload size limit - 5 MB only when Content-Length header is provided.
# Catalog Overview
Source: https://moengage.com/docs/api/catalog/catalog-overview
Manage product and item catalogs, define attributes, and handle bulk item ingestion.
The MoEngage Catalog API allows you to manage product and item catalogs. Use these endpoints to create new catalogs, define custom schemas, and perform bulk operations for ingesting, updating, or deleting items.
* File-based catalogs and API-based catalogs are not interoperable. You cannot update a file-based catalog through the API, or update an API-based catalog through a file upload.
* MoEngage recommends using the Item ID, not the Variation ID, as the `id` field when you set up a catalog. If your use case requires the Variation ID as the identifier, contact your Customer Success Manager.
## Endpoints
The Catalog API is a collection of the following endpoints:
* [Create Catalog](/docs/api/catalog/create-catalog): Creates a new catalog with a unique name.
* [Add Catalog Attributes](/docs/api/catalog/add-catalog-attributes): Adds new attributes to the catalog.
* [Add Items](/docs/api/items/add-items): Ingests items into an existing catalog.
* [Get Items](/docs/api/items/get-items): Retrieve item attribute details.
* [Update Items](/docs/api/items/update-items): Updates items with new attribute values.
* [Delete Items](/docs/api/items/delete-items): Deletes existing items in a given catalog.
## FAQs
### Catalog Management
The API will return a `409 Conflict` error with the error code `duplicate-catalog-name`. Catalog names must be unique within your workspace.
Yes, you can use the **Add Catalog Attributes** (PATCH) endpoint. Note that if you send attributes that already exist in the schema, they will be ignored and listed in the `duplicate-item-attributes` array in the response.
You can define up to 50 attributes per catalog, including the 4 mandatory attributes.
API catalogs automate real-time updates such as price and quantity. They offer improved efficiency and scalability by updating only specific products, unlike file-based catalogs, which require the replacement of the whole file at each processing schedule. These APIs can be accessed and used to update from any location.
Each error code is a uniquely defined shorthand representation for the type of error, providing a quick reference that can be used to diagnose, troubleshoot, and address the problem based on a predefined set of error conditions.
No, the datatype cannot be changed once an attribute has been defined in the catalog. You can add a new attribute to the catalog with a different datatype as needed.
A maximum of 50 attributes can be added to the catalog.
Before updating existing items with new attributes, you must first add the new attribute to the catalog schema using the 'Add catalog attribute' API. Only then will your update requests for including new attributes be successful.
The request will be processed successfully marking such items as invalid. The response will include the count of these invalid item IDs.
### Item Ingestion and Updates
The API returns a `200 OK` with a detailed breakdown. The response body will include a `valid` count and an `invalid` count with a `details` array specifying which `document_ids` failed and why (e.g., data type mismatch or missing attributes).
No. The `id` is the primary key. To change an ID, you must delete the existing item and ingest it as a new item.
Yes, the bulk-delete endpoint supports a maximum of 50 item IDs per request.
All MoEngage catalog APIs operate synchronously. This means that every catalog API request is processed in real time, and you can see the changes instantly.
### Get Item Details
Each error code is a uniquely defined shorthand representation for the type of error, providing a quick reference that can be used to diagnose, troubleshoot, and address the problem based on a predefined set of error conditions.
No, the data type cannot be changed once an attribute has been defined in the catalog. You can add a new attribute to the catalog using the [Add new catalog attribute API](/docs/api/catalog/add-catalog-attributes) with a different data type as needed.
## Postman Collections
Test these endpoints immediately using our Postman collection. [View Postman Collection](https://www.postman.com/moengage-dev/api-docs/collection/seck3f6/moengage-catalog-api)
# Create Catalog
Source: https://moengage.com/docs/api/catalog/create-catalog
/api/catalog/catalog.yaml post /catalog
This API creates a new catalog with a unique name. You can specify the necessary attributes along with their respective data types.
#### Rate Limit
* Request limit: You can create 100 catalogs per minute OR 1000 catalogs per hour.
* Payload size limit: 5 MB only when Content-Length header is provided.
# Sync Cohort Members
Source: https://moengage.com/docs/api/cohort-sync/sync-cohort-members
/api/cohort-audience/cohort-audience.yaml post /v1/integrations/cohortsync
This API adds or removes a list of users from a custom segment (cohort) in MoEngage.
* The Cohort Sync API only matches users already present in MoEngage based on the User ID (`uid`) provided in the request. This API does not create new users.
* This API endpoint does not currently support Team-level scoping. All segments generated using this call will be assigned to the Default Team automatically.
* Please note that this API does not support updating cohorts created on the dashboard or via [File Segment API](https://www.moengage.com/docs/api/file-segments/create-file-segment).
#### Rate Limits
Please adhere to the following limits:
* **Frequency:** 300 requests per minute.
* **Payload Structure:** One payload per request (each payload can contain multiple UIDs).
* **Size Limit:** The payload size cannot exceed **128KB**.
#### User Resolution
The Cohort Sync API resolves existing users in MoEngage based on predefined user identifiers:
* **Identifier:** We primarily rely on the **User ID** (`uid`) provided in the request payload.
* **Matching:** This must match the Unique User ID set in MoEngage (typically available for registered or logged-in users).
* **Profile Association:** Each Unique User ID corresponds to a single user profile. Even if a user is logged in across multiple devices, the devices are associated with that single user profile.
#### Cohort Size Limits
A cohort you sync through this API is a custom segment, which is a type of file segment. Each segment supports up to 5 million users. MoEngage stops updating a segment once it exceeds 10 million users.
#### Segment Archival
Cohorts you sync through this API follow the same lifecycle as file segments:
* MoEngage automatically archives a segment after 60 days of inactivity.
* You cannot unarchive a segment created through Cohort Sync.
# Content APIs Overview
Source: https://moengage.com/docs/api/content-apis/content-apis-overview
List the content APIs configured in your workspace and test a saved configuration against its upstream endpoint.
A content API is an external endpoint that MoEngage calls to pull dynamic data into a campaign at send time. For example, you can use one to fetch the current weather in a user's city, the live price of an item in their cart, or the status of their flight.
The MoEngage Content APIs allow you to list the content APIs configured in your workspace and test a saved configuration against its upstream endpoint. To create or edit a content API, use the MoEngage dashboard. For more information, see [Add a Content API](/docs/user-guide/settings/advanced-settings/add-a-content-api).
## Endpoints
The Content APIs are a collection of the following endpoints:
* [List Content APIs](/docs/api/content-apis/list-content-apis): Returns all content APIs in the workspace, or a single one specified by name or ID.
* [Test Content API](/docs/api/content-apis/test-content-api): Calls the upstream endpoint of a saved content API and returns the response.
## Authentication
Authentication is done via Basic Auth. This requires a Base64-encoded string of your credentials in the format `username:password`.
* **Username**: Use your MoEngage Workspace ID (also known as the App ID). Find it in the MoEngage dashboard at **Settings** > **Account** > **API keys**.
* **Password**: Use an API key from **Settings** > **Account** > **API keys**.
Refer to [API Key Dashboard](/docs/user-guide/settings/account/api-and-api-keys/api-key-dashboard) for details on creating and managing API keys.
## Personalization Tokens
A content API configuration can include Jinja tokens in its URL, parameters, headers, or body. Tokens are grouped by namespace — for example, `{{UserAttribute['city']}}` or `{{EventAttribute['name']}}`.
When you test a configuration, supply sample values for these tokens in the `dynamic_values` object of the request body, keyed by namespace. A token like `{{UserAttribute['city']}}` resolves from `dynamic_values.UserAttribute.city`. MoEngage resolves every token before calling the upstream endpoint.
Omit the request body if the configuration has no tokens.
## Pagination
[List Content APIs](/docs/api/content-apis/list-content-apis) returns at most 20 items per page. Treat the cursor as opaque — do not decode or modify it.
To page through every content API in the workspace:
1. Request the first page with `limit` alone.
```bash First Page theme={null}
curl --request GET \
--url 'https://api-01.moengage.com/v5/content-apis?limit=20' \
--header 'Authorization: Basic '
```
2. Check `pagination.has_more` in the response. When it is `true`, send the same request again with `pagination.next_cursor` passed back as the `cursor` parameter, keeping `limit` unchanged.
```bash Next Page theme={null}
curl --request GET \
--url 'https://api-01.moengage.com/v5/content-apis?limit=20&cursor=eyJsYXN0X2lkIjoiNjZiM2QxZTBmMmE0YzU4ZTlkN2IzYzIxIn0=' \
--header 'Authorization: Basic '
```
3. Repeat step 2 until `pagination.has_more` is `false`.
## FAQs
### Manage Content APIs
No. These endpoints are read-only, apart from the test call. Create and edit content APIs from the MoEngage dashboard. For more information, see [Add a Content API](/docs/user-guide/settings/advanced-settings/add-a-content-api).
Pass either `name` or `id` to [List Content APIs](/docs/api/content-apis/list-content-apis). Supplying both returns a `400` error. Omit both to list every content API in the workspace.
A request body applies only to `POST` and `PUT` content APIs. For `GET` configurations, `request_body` is empty and `request_body_type` does not affect the call.
`verified` becomes `true` once the content API has returned a successful test response. `last_tested_at` records when that test ran.
### Test Content APIs
No. The request body is optional. Send it only when the saved configuration contains Jinja tokens that need sample values. Omit it entirely otherwise.
A `200` means MoEngage reached the upstream endpoint — it does not mean the upstream call succeeded. Check `data.api_response_code` for the status code the upstream endpoint returned, and `data.api_response_body` for its response.
Either the request body failed validation, or the saved configuration's URL resolves to an internal or private IP range. MoEngage validates the URL against server-side request forgery (SSRF) and rejects those addresses.
List them in the content API's `pii_fields_in_response` configuration. Fields marked this way are treated as personally identifiable information (PII).
## Postman Collection
Test these endpoints using our pre-configured Postman collection: [View MoEngage Content APIs Collection](https://www.postman.com/moengage-dev/api-docs/collection/wssclnq/moengage-content-api-v5?action=share\&source=copy-link\&creator=3486165).
# List Content APIs
Source: https://moengage.com/docs/api/content-apis/list-content-apis
/api/content-apis/content-apis.yaml get /v5/content-apis
Returns all content APIs in the workspace, or a single one when you supply `name` or `id`. Use either `name` or `id`, not both. Omit both parameters to list every content API in the workspace (paginated).
#### Rate Limits
The rate limits are at the workspace level. A maximum of 10 requests per minute and 100 requests per hour are allowed per workspace.
# Test Content API
Source: https://moengage.com/docs/api/content-apis/test-content-api
/api/content-apis/content-apis.yaml post /v5/content-apis/{id}/test
Loads the saved content API identified by `id` and executes it against the upstream endpoint, returning the response. The full definition — URL, method, parameters, headers, body, and authentication — comes from the saved configuration.
The optional request body supplies sample values for the personalization tokens (`{{UserAttribute['city']}}`) in that configuration, which are resolved before the call. Omit the body if the configuration has no tokens.
Send `Content-Type: application/json` only when you include a request body. When the saved configuration has no tokens and you send no body, omit the header as well.
The upstream response is passed through in `data.api_response_body` — parsed JSON when the upstream returns JSON, and the raw body as a string otherwise. See the response examples for both cases.
The URL is validated against server-side request forgery (SSRF): internal and private ranges are rejected with `400`.
A well-formed `id` that matches no saved configuration returns `404`. An `id` that is not a valid ObjectId returns `400` with `VALIDATION_FAILED`.
#### Rate Limits
The rate limits are at the workspace level. A maximum of 10 requests per minute and 100 requests per hour are allowed per workspace.
# Content Blocks Overview
Source: https://moengage.com/docs/api/content-blocks/content-blocks-overview
Fetch, create, and manage reusable content blocks.
Content blocks allow marketers to reuse the same content across multiple campaigns. Instead of recreating standard elements for every message, you can create a block once—such as a header, footer, or a designed call-to-action button—and reference it everywhere.
This MoEngage Content Block API allows you to fetch, create, and update these content blocks on your MoEngage dashboard.
There's no hard cap on the number of content blocks you can create in a workspace. However, editor performance can start to degrade once a workspace has more than approximately 30,000 content blocks. MoEngage recommends archiving or deleting content blocks that are no longer in use as you approach this scale.
## Endpoints
The Content Block API is a collection of the following endpoints:
* [Create Content Block](/docs/api/content-blocks/create-content-block): Creates a content block.
* [Update Content Block](/docs/api/content-blocks/update-content-block): Updates a content block specified by its ID.
* [Get Specific Content Blocks](/docs/api/content-blocks/get-specific-content-blocks): Fetches specific content blocks from the available content blocks.
* [Search Content Blocks](/docs/api/content-blocks/search-content-blocks): Searches the content blocks created in your MoEngage account.
## Postman Collections
We have made it easy for you to test the APIs. Click [here](https://www.postman.com/moengage-dev/api-docs/collection/3486165-5dc87a9b-a3ef-442f-9d45-512253c6b891) to view our official Postman collections.
# Create Content Block
Source: https://moengage.com/docs/api/content-blocks/create-content-block
/api/content-blocks/content-blocks.yaml post /content-blocks
This API creates a content block in MoEngage.
Use the `source` field in the request body to specify the communication type for the content block:
- `"source": "CAMPAIGNS"`: Creates a content block for use in MoEngage campaigns.
- `"source": "INFORM"`: Creates a content block for use in Inform alerts.
# Get Specific Content Blocks
Source: https://moengage.com/docs/api/content-blocks/get-specific-content-blocks
/api/content-blocks/content-blocks.yaml post /content-blocks/get-by-ids
This API retrieves specific content blocks from the available content blocks in your MoEngage account.
# Search Content Blocks
Source: https://moengage.com/docs/api/content-blocks/search-content-blocks
/api/content-blocks/content-blocks.yaml post /content-blocks/search
This API searches for the available content blocks in your MoEngage account.
# Update Content Block
Source: https://moengage.com/docs/api/content-blocks/update-content-block
/api/content-blocks/content-blocks.yaml put /content-blocks
This API updates the content blocks in MoEngage.
Updates are not always instant. For large content blocks, the updated state can take up to ~30 minutes to propagate to active event-triggered and flow sends. Small content blocks typically refresh near-instantly.
# Delete a Coupon File from the Coupon List
Source: https://moengage.com/docs/api/coupon-files/delete-a-coupon-file-from-the-coupon-list
/api/coupons/coupons.yaml delete /coupon-list/{coupon_list_id}/files/{coupon_file_id}
This API removes a specific coupon file from a coupon list. It is useful in scenarios where a test file or incorrect file is inadvertently uploaded, thereby ensuring the accuracy and effectiveness of your coupon list management.
**Information**
There is no request body or content to send for this request except for headers.
#### Rate Limit
You can delete:
* 5 coupon files per minute or
* 50 coupon files per day
# Fetch a Coupon File from Coupon List
Source: https://moengage.com/docs/api/coupon-files/fetch-a-coupon-file-from-coupon-list
/api/coupons/coupons.yaml get /coupon-list/{coupon_list_id}/files/{coupon_file_id}
This API retrieves the details of a particular coupon file added to a given coupon list. This includes information such as file status, the number of added coupons, and the file addition date.
**Information**
There is no request body or content to send for this request except for headers.
#### Rate Limit
You can fetch 10,000 coupon files from a coupon list per day.
# Fetch All Coupon Files From Coupon List
Source: https://moengage.com/docs/api/coupon-files/fetch-all-coupon-files-from-coupon-list
/api/coupons/coupons.yaml get /coupon-list/{coupon_list_id}/files
This API retrieves the details of each file in the given coupon list that is added and not deleted. The information retrieved will include the file's status, the number of coupons added from each file, and the respective file's addition date, thereby providing a comprehensive breakdown of each file's information for improved management and tracking.
There is no request body or content to send for this request except for headers.
#### Rate Limit
You can fetch 10,000 coupon files from coupon lists per day.
# Upload a Coupon File to the Coupon List
Source: https://moengage.com/docs/api/coupon-files/upload-a-coupon-file-to-the-coupon-list
/api/coupons/coupons.yaml post /coupon-list/{coupon_list_id}/files
After you create a coupon list, you must add coupons to the list to be distributed through campaigns. If a coupon list has been running for some time, it may be running low after serving several campaigns.
Using this API, you can replenish an older list or populate a new list by providing the URL of a file containing the coupons, thereby enabling their distribution through various campaigns. These coupons can be provided through a file, and the API requires the URL where your coupon file is hosted.
**Information**
Upon API request, file processing begins, and the file status will be **PENDING** by default.
* For convenience, you can set up a callback URL to trigger when the file's processing is completed.
* You can check the processing status using the [Fetch a Coupon File API](https://www.moengage.com/docs/api/coupon-files/fetch-a-coupon-file-from-coupon-list) to get the status separately.
#### Rate Limit
You can upload:
* 5 coupon files to a coupon list per minute or
* 50 coupon files to a coupon list per day
**Note:**
* **Payload size limit**: 64 MB for manual uploads or 100 MB for URL uploads.
* **Additional limits**: The maximum number of coupons per coupon list is 100 million.
# Activate Coupon List
Source: https://moengage.com/docs/api/coupon-lists/activate-coupon-list
/api/coupons/coupons.yaml put /coupon-list/{coupon_list_id}/activate
This API reactivates archived coupon lists, provided the expiry date is in the future.
**Information**
* Only active coupon lists can be utilized in campaigns.
* If you need to modify the expiry date and activate a coupon list, you must use the [Update a Coupon List API](#operation/updateCouponList).
#### Rate Limit
You can activate 100 coupon lists per day.
# Archive a Coupon List
Source: https://moengage.com/docs/api/coupon-lists/archive-a-coupon-list
/api/coupons/coupons.yaml put /coupon-list/{coupon_list_id}/archive
This API transitions an active coupon list to an archived status. Upon archival, the coupon codes within the list are deleted. Consequently, any campaigns that were previously dependent on this list will no longer be able to utilize the dynamic coupon allocation.
**Information**
Verify the usage of a given coupon list in active or planned campaigns before archiving it.
#### Rate Limit
You can archive 100 coupon lists per day.
# Create a Coupon List
Source: https://moengage.com/docs/api/coupon-lists/create-a-coupon-list
/api/coupons/coupons.yaml post /coupon-list
This API creates single-use coupon codes. You can use this API to create and organize distinct lists for different coupon code categories.
**Information**
This API creates the coupon list with basic specifications only. The coupons should be added using [Upload the Coupons](https://www.moengage.com/docs/api/coupon-files/upload-a-coupon-file-to-the-coupon-list) API to this coupon list before utilizing it in campaigns.
#### Rate Limit
You can create 100 coupon lists per day.
# Fetch All Coupon Lists
Source: https://moengage.com/docs/api/coupon-lists/fetch-all-coupon-lists
/api/coupons/coupons.yaml get /coupon-list
This API fetches all created coupon lists in a specific workspace. By default, this API returns the coupon lists marked with an *ACTIVE* status. In return, it offers detailed specifications of the active coupon lists, respective configurations, statuses, expiry dates, and alert conditions, including on-time data on the total coupons added and those that are currently available.
**Information**
There is no request body or content to send for this request except for the headers and parameters.
#### Rate Limit
You can fetch 10,000 coupon lists per day.
# Fetch Coupon List Details
Source: https://moengage.com/docs/api/coupon-lists/fetch-coupon-list-details
/api/coupons/coupons.yaml get /coupon-list/{coupon_list_id}
This API retrieves the specifications of a particular coupon list. It includes information such as configurations, statuses, expiry dates, and alert conditions with real-time counts of added and currently available coupons. Using this API, you can easily access and manage critical data about individual coupon lists.
**Information**
There is no request body or content to send for this request except for headers and parameters.
#### Rate Limit
You can fetch 10000 coupon lists per day.
# Update a Coupon List
Source: https://moengage.com/docs/api/coupon-lists/update-a-coupon-list
/api/coupons/coupons.yaml patch /coupon-list/{coupon_list_id}
This API modifies existing coupon lists within a defined workspace. It facilitates changes to specifications like list name, expiry date, and alert settings, thereby promoting efficient coupon operations management.
**Information**
This API reactivates the *ARCHIVED* or *EXPIRED* coupon list upon modification, provided the list has a future expiration date.
#### Rate Limit
You can update 100 coupon lists per day.
# Coupon Management Overview
Source: https://moengage.com/docs/api/coupons/coupons-overview
Manage unique coupon lists, upload coupon codes, and generate usage reports within the MoEngage system.
The MoEngage Coupon Management API allows you to organize and distribute unique, single-use coupon codes at scale. Use these endpoints to automate the lifecycle of coupon lists—from creation and file uploads to activation, archival, and detailed usage reporting.
## Endpoints
The Coupon Management API is categorized into three functional areas:
### Coupon Lists (Metadata and Status)
* [Create a Coupon List](/docs/api/coupon-lists/create-a-coupon-list): Defines a new category for unique codes.
* [Fetch All Coupon Lists](/docs/api/coupon-lists/fetch-all-coupon-lists): Retrieves a list of active or archived coupon containers.
* [Fetch Coupon List Details](/docs/api/coupon-lists/fetch-coupon-list-details): Retrieves real-time counts of available vs. total coupons.
* [Update a Coupon List](/docs/api/coupon-lists/update-a-coupon-list): Modifies names, expiry dates, or alert settings.
* [Activate Coupon List](/docs/api/coupon-lists/activate-coupon-list): Reactivates archived lists with a new expiry date.
* [Archive a Coupon List](/docs/api/coupon-lists/archive-a-coupon-list): Transitions a list to inactive and deletes associated codes.
### Coupon Files (Inventory Management)
* [Upload Coupon File](/docs/api/coupon-files/upload-a-coupon-file-to-the-coupon-list): Populates a list via a hosted file URL.
* [Fetch All Coupon Files](/docs/api/coupon-files/fetch-all-coupon-files-from-coupon-list): Tracks the status of all uploaded batches.
* [Fetch a Coupon File](/docs/api/coupon-files/fetch-a-coupon-file-from-coupon-list): Checks the processing status (e.g., PENDING) of a specific file.
* [Delete a Coupon File](/docs/api/coupon-files/delete-a-coupon-file-from-the-coupon-list): Removes specific batches (useful for correcting upload errors).
### Reports
* [Generate Usage Report](/docs/api/reports/generate-usage-report): Generates a detailed usage report for a specific coupon list, providing details on which user received which coupon.
## Postman Collections
We have made it easy for you to test the APIs. Click [here](https://www.postman.com/moengage-dev/api-docs/collection/y1e45ee/moengage-coupon-management) to view our official Postman collections.
# Create Campaign
Source: https://moengage.com/docs/api/create-campaigns/create-campaign
/api/campaigns/campaigns.yaml post /campaigns
This API creates a new Push or Email campaign in MoEngage with specified content, audience, and delivery settings.
#### Rate Limits
| Rate Limit Name | Rate Limit |
| :------------------------- | :------------------------------------------------------------------------------- |
| Create campaign per minute | The total number of create campaign requests per minute per client allowed is 5. |
| Create campaign per hour | The total number of create campaign requests per hour per client allowed is 25. |
| Create campaign per day | The total number of create campaign requests per day per client allowed is 100. |
### Campaign Creation Limits
You can create 5 campaigns per minute, 25 campaigns per hour, and 100 campaigns per day.
**Notes**
* Higher limit (Total Calls): The system permits a higher volume of total API calls (for example, 120 calls) to accommodate potential failures or retries.
* Minimum limit (Campaign Creation Limits): The system maintains a stricter quota for actual successful creations (100 campaigns per day).
Example: If a client submits 120 requests and 20 fail, they successfully generate exactly 100 campaigns. Because the system applies the most restrictive threshold, the 100 successful operations trigger the quota limit, and the system issues a rate limit breach warning regardless of the total API calls made.
* Breaching the limits will reject the request.
* Per hour and per day limits will consider the calculation based on the last hour and last 24 hrs respectively.
# Create Campaign Draft (V5)
Source: https://moengage.com/docs/api/create-campaigns/create-campaign-draft-v5
/api/campaigns/campaign-draft.yaml post /v5/campaigns
Creates a Push or Email campaign draft. Content, audience, and delivery settings can be included at the time of creation, or added later via `PATCH /v5/campaigns/{campaign_id}`.
**Component reference pages:**
* For the full schema of `basic_details` and `campaign_content` per channel, platform, and template type :
* Android (`BASIC` / `STYLIZED_BASIC` / `SIMPLE_IMAGE_CAROUSEL` / `IMAGE_BANNER_WITH_TEXT` / `TIMER` / `TIMER_WITH_PROGRESS_BAR` / `Custom`)
* iOS (`BASIC` / `STYLIZED_BASIC` / `SIMPLE_IMAGE_CAROUSEL` / `Custom`)
* Web `BASIC`
* Email (`html_content` and `custom_template_id`), see [Campaign content reference](/docs/api/campaigns/campaign-content-reference).
* For the full schema of `trigger_condition`, `segmentation_details`, `scheduling_details`, `delivery_controls`, `conversion_goal_details`, `control_group_details`, `utm_params`, `campaign_audience_limit`, `advanced`, and `geofences`, see [Audience and delivery reference](/docs/api/campaigns/audience-scheduling-delivery-reference).
#### Rate Limits
| Rate Limit Name | Rate Limit |
| :------------------------- | :--------------------------------------------------------------------------------- |
| Create campaign per minute | The total number of create campaign operations per minute per client allowed is 5. |
| Create campaign per hour | The total number of create campaign operations per hour per client allowed is 25. |
| Create campaign per day | The total number of create campaign operations per day per client allowed is 100. |
### Campaign Creation Limits
You can create 5 campaigns per minute, 25 campaigns per hour, and 100 campaigns per day.
**Notes**
* Higher limit (Total Calls): The system permits a higher volume of total API calls (for example, 120 calls) to accommodate potential failures or retries.
* Minimum limit (Campaign Creation Limits): The system maintains a stricter quota for actual successful creations (100 campaigns per day).
Example: If a client submits 120 requests and 20 fail, they successfully generate exactly 100 campaigns.
Because the system applies the most restrictive threshold, the 100 successful operations trigger the quota limit, and the system issues a rate limit breach warning regardless of the total API calls made.
* Breaching the limits will reject the request.
* Per-hour and per-day limits use a rolling window of the last 1 hour and last 24 hours respectively.
# Validate Campaign (V5)
Source: https://moengage.com/docs/api/create-campaigns/validate-campaign-v5
/api/campaigns/campaign-draft.yaml post /v5/campaigns/{campaign_id}/validate
Runs full publish-time validation (`DRAFT_PUBLISH`) on a saved draft without mutating it. Returns `valid: true` if the campaign would pass, or a list of blocking errors if it would not.
All campaign data is read from the saved draft identified by `campaign_id` in the path. There is no channel-specific payload.
You do not need an idempotency key for this endpoint. The validate endpoint is read-only and safe to retry freely.
### Validation Modes
The V5 API applies validation in two modes:
| Mode | Triggered by | Strictness |
| :--------------------- | :----------------------------------------------------------------------------------- | :----------------------------------------------------------------------------------- |
| Lenient | Draft create (`POST /v5/campaigns`) and component patch (`PATCH /v5/campaigns/{id}`) | Partial — individual components are validated in isolation as they are written |
| Full (`DRAFT_PUBLISH`) | `POST /v5/campaigns/{id}/validate` and publish | Strict — all components are validated together as a complete, publish-ready campaign |
Use this endpoint to catch `DRAFT_PUBLISH` failures before triggering a publish.
### Validation Failures
All validation failures are blocking. There is no warning or non-blocking tier. A campaign that fails validation returns `valid: false` with a list of field-level errors. Each error identifies the `field` path and the `issue`.
### Channel-Specific Rules
The following rules are enforced at `DRAFT_PUBLISH` for each channel:
**Push**
* `campaign_content` must be present and include content for all platforms listed in `basic_details.platforms`.
* `template_type` must be valid for the target platform (Android, iOS, Web).
* Platform-specific required fields (for example, `title` and `message` for Android basic templates) must be non-empty.
**Email**
* `connector.connector_type` and `connector.connector_name` must be present and match a connector configured in your workspace.
* `basic_details.from_address` must be set and match a verified sender in the configured connector.
* `basic_details.subscription_category` must be present for `PROMOTIONAL` content type.
* `campaign_content` must include at least one variation with a non-empty `subject` and `html_content`.
### Delivery-Type-Specific Rules
The following rules are enforced at `DRAFT_PUBLISH` based on `campaign_delivery_type`:
| Delivery Type | Additional Requirements |
| :------------------------- | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `ONE_TIME`, `PERIODIC` | `scheduling_details` must be present. |
| `AT_FIXED_TIME` scheduling | `scheduling_details.timezone` must be present. |
| `PERIODIC` | `scheduling_details.periodic_details` must be present with a valid `sending_frequency`. |
| `EVENT_TRIGGERED` | `trigger_condition` must be present with at least one `included_filters` entry. |
| `DEVICE_TRIGGERED` | `trigger_condition` must be present. Trigger delay fields (`trigger_delay_type`, `trigger_delay_value`, `trigger_delay_granularity`) must **not** be set — they are only valid for `EVENT_TRIGGERED`. |
| `BUSINESS_EVENT_TRIGGERED` | `basic_details.business_event` must be present. |
| `LOCATION_TRIGGERED` | `basic_details.geofences` must contain at least one valid geofence entry. |
#### Rate Limits
| Rate Limit Name | Rate Limit |
| :--------------------------- | :----------------------------------------------------------------------------------- |
| Validate campaign per second | The total number of validate campaign requests per second per client allowed is 10. |
| Validate campaign per minute | The total number of validate campaign requests per minute per client allowed is 100. |
| Validate campaign per hour | The total number of validate campaign requests per hour per client allowed is 6000. |
# Segments Overview
Source: https://moengage.com/docs/api/custom-segments/custom-segments-overview
Create, update, and manage your file-based, filter-based, and cohort-synced segments.
The MoEngage Segments API allows you to manage your audience segments. This suite includes the **v2 API** for handling high-volume file-based segments and segment lifecycles, the **v3 API** for dynamic, filter-based segments, and the **Cohort Sync API** for synchronizing external audiences directly with MoEngage.
## Endpoints
The Segments API is a collection of the following endpoints:
### File Segments (v2)
The File Segments API allows you to create and manage segments by importing users from CSV files hosted at a URL.
* [Create File Segment](/docs/api/file-segments/create-file-segment): Creates a new file segment from a CSV file URL.
* [Add Users to File Segment](/docs/api/file-segments/add-users-to-file-segment): Adds a list of users from a CSV file to an existing file segment.
* [Remove Users from File Segment](/docs/api/file-segments/remove-users-from-file-segment): Removes a list of users from a CSV file from an existing file segment.
* [Replace Users in File Segment](/docs/api/file-segments/replace-users-from-file-segment): Replaces all users in an existing file segment with a new list of users from a CSV file.
Ensure all users are imported into MoEngage before including them in a file segment; only existing profiles can be successfully mapped.
File segments archived for more than 30 days will undergo permanent user deletion. This data cannot be recovered once deleted.
### Manage Segments (v2)
The Manage Segments API allows you to control the lifecycle of your segments by archiving or unarchiving them.
* [Archive Segment](/docs/api/manage-segments/archive-segment): Archives an existing segment (File or Filter).
* [Unarchive Segment](/docs/api/manage-segments/unarchive-segment): Unarchives an existing segment, making it active again.
### Filter Segments (v3)
The Filter Segments API allows you to create and manage dynamic segments based on user attributes and behavioral filter conditions.
* [List Segments](/docs/api/filter-segments/list-segments): Lists all filter segments.
* [Create Filter Segment](/docs/api/filter-segments/create-filter-segment): Creates a new filter segment based on a set of filter conditions.
* [Get Segment by ID](/docs/api/filter-segments/get-segment-by-id): Fetches a specific segment (File or Filter) by its ID.
* [Update Filter Segment](/docs/api/filter-segments/update-filter-segment): Updates an existing filter segment by its ID.
### Cohort Sync
The Cohort Sync API allows you to synchronize cohorts or audiences created in your own ecosystem directly with MoEngage. This server-to-server integration enables you to add or remove users from custom segments dynamically, ensuring your marketing campaigns always target the most relevant audience.
* **Automated Segment Creation:** Automatically creates a custom segment in MoEngage if it doesn't already exist.
* **Dynamic Membership:** Real-time updates to segments allow scheduled campaigns to engage the latest set of users.
* **No Middleware Needed:** Direct server-to-server calls remove the need for manual CSV uploads or hosting external URLs.
* **Increased Efficiency:** Ideal for segment operations involving smaller subsets of users.
**Endpoint:**
* [Sync Cohort Members](/docs/api/cohort-sync/sync-cohort-members): Adds or removes users from a custom segment.
These API endpoints do not currently support Team-level scoping. All segments generated using these calls will be assigned to the Default Team automatically.
## FAQs
### Filter-Based Segments
You can generate the payload directly from the MoEngage Dashboard. Navigate to **Test & Debug** -> **Segment Payload**, choose your filters, and click **Generate Payload**.
Use the **List Segment API** with the `name` query parameter to filter and retrieve the unique ID of the desired segment.
Both the name and the definition (filters) of a segment must be unique. If the definition matches an existing segment, the API returns a 409 Conflict.
In the case of a 409 error, the response payload includes the `existing_cs_name` and `existing_cs_id` of the conflicting segment.
Please connect with your account manager.
### File-Based Segments
Segment processing is asynchronous. MoEngage first creates the segment container (showing zero users) and then processes the file. The count will update once processing is complete.
No. There is no fixed processing timeout. MoEngage processes the file until it completes successfully or encounters an error, then sends the result to your `callback_url`.
If the initial file download fails, MoEngage automatically retries before reporting a failure via the callback. The callback payload will include an `error_message` field describing the failure.
No. File segments can only be updated (add/remove/replace) via the File Segment API using CSV imports. For attribute-based updates, use Filter Segments.
### Manage Segments
Archiving allows you to reuse segments for A/B testing or historical analysis without recreating them from scratch, while keeping your active segment list within the 1000-segment limit.
### Cohort Sync
The Cohort Sync API enables the creation of segments without needing separate files or CSVs, eliminating the requirement for a separate path or URL as seen in the File Segment API. Segment creation can be initiated by making a direct server-to-server call, eliminating the additional steps required for generating user files.
The Cohort Sync API is ideal for segments or segment operations involving fewer users.
The Cohort Sync API does not create new users in MoEngage. Instead, it resolves existing users in MoEngage based on predefined user identifiers and assigns them to the corresponding custom segment created through Cohort Sync.
## Postman Collections
Test these endpoints quickly by importing our Postman collections: [File Segments](https://www.postman.com/moengage-dev/api-docs/collection/z8f27qa/moengage-custom-segment-api), [Filter Segments](https://www.postman.com/moengage-dev/api-docs/collection/s16aovr/moengage-custom-segment-filter-based-crud-api-s), and [Cohort Sync](https://www.postman.com/moengage-dev/api-docs/collection/2siizsq/moengage-cohort-audience-sync-revamp?action=share\&creator=3182294) in Postman.
# Get Chart Data
Source: https://moengage.com/docs/api/dashboards/get-chart-data
/api/analytics/analytics.yaml get /v5/analytics/dashboards/{dashboard_id}/charts/{chart_id}
Returns the data behind a single chart, as an array of rows. Each call runs the chart's analytics query and returns the result.
**About the chart data:**
* The data matches what the chart shows in the MoEngage dashboard. It uses the chart's saved settings, such as its date range, segment, filters, and breakdowns. You can't change these settings through the API, because the endpoint accepts no date-range or segment parameters.
* By default, the response is served from a server-side cache. To recompute the chart with the latest data, set the `cache` query parameter to `false`.
* The fields in each row depend on the chart's analysis type: Behavior, Funnels, Retention, User, or Session and Source. To learn more about these analysis types, see [MoEngage Analytics](/docs/user-guide/analyze/moengage-analytics/overview).
# Get Dashboard Charts
Source: https://moengage.com/docs/api/dashboards/get-dashboard-charts
/api/analytics/analytics.yaml get /v5/analytics/dashboards/{dashboard_id}/charts
Returns the details of a single dashboard, including its name, creator, and owner. It also returns the list of charts on the dashboard, in layout order. Each chart entry contains the chart's ID and name.
Chart data is not returned here. To fetch the data for a chart, use [Get Chart Data](/docs/api/dashboards/get-chart-data) with the dashboard ID and chart ID.
# List Dashboards
Source: https://moengage.com/docs/api/dashboards/list-dashboards
/api/analytics/analytics.yaml get /v5/analytics/dashboards
Returns the custom dashboards available to the authenticated workspace. Archived dashboards are excluded.
Only workspace-level (public) dashboards are returned. Private dashboards are not available through this API.
# Data Overview
Source: https://moengage.com/docs/api/data/data-overview
Manage users, track events, handle devices, and perform bulk data operations.
The MoEngage Data API provides a comprehensive suite of endpoints designed to help you manage data within MoEngage. This API enables you to create and update user profiles, track user actions (events), manage device information, and handle large-scale data ingestion via bulk and file import operations.
## Endpoints
The Data API is a collection of the following API endpoints:
* [Track User](/docs/api/user/track-user): Adds or updates users and user properties in MoEngage.
* [Get User](/docs/api/user/get-user): Facilitates the retrieval of information of users.
* [Merge User](/docs/api/user/merge-users): Merges two users in MoEngage based on their ID.
* [Delete User](/docs/api/user/delete-users): Deletes users in MoEngage.
* [Track Event](/docs/api/event/track-event): Tracks the actions of a user.
* [Track Device](/docs/api/device/track-device): Adds or updates devices and device properties in MoEngage.
* [Device Opt-out](/docs/api/device/device-opt-out): Blocks or unblocks specific devices from receiving push notifications.
* [Trigger File Imports](/docs/api/file-import/trigger-file-imports): Triggers scheduled file imports.
* [Import Details](/docs/api/file-import/import-details): Fetches the status at an import level.
* [Import File Run History](/docs/api/file-import/import-file-run-history): Fetches the file processing status of each file contained in an import.
* [Bulk Import](/docs/api/bulk/bulk-import-users-and-events): Sends multiple user and event requests in batch to MoEngage.
* [Install Tracking](/docs/api/tracking/track-app-install): Tracks the install attribution data in MoEngage.
* [Test connection](/docs/api/utilities/test-connection-api): Validates if the entered endpoint details are valid.
- For workspaces in MoEngage with the *User Identity Resolution* feature enabled, use the following Data APIs to create or update users using a specific identifier, such as a mobile number or email ID, as configured in **Settings** > **Data** > **Identity Resolution**:
* Track User
* Create Event
* Bulk Import
For more information, refer to [User Identity Resolution](https://help.moengage.com/hc/en-us/articles/24050999467284-Unified-Identity-Identity-Resolution).
You can:
* Create users through Server-to-Server Data APIs even when they do not have an ID (but have other identifiers).
* Create a user or track events of a user when identifiers other than ID (for example, email ID or phone number) are known.
- Data APIs support [IP whitelisting](/docs/user-guide/settings/account/security/ip-whitelisting-in-moengage). Contact the [MoEngage support team](/docs/user-guide/contact-support/raise-a-support-ticket-through-moengage-dashboard) to whitelist your IP addresses. After configuration, MoEngage exclusively ingests API payloads originating from these whitelisted IPs.
## Request Body
The request body contains the mandatory field called `customer_id`. It is the unique identifier set and passed on from the MoEngage SDK as `USER_ATTRIBUTE_UNIQUE_ID` and is visible on the dashboard as `ID`.
`customer_id` is used to:
* Identify or create a user in MoEngage.
* Associate the events with the corresponding unique user profiles in MoEngage.
On receiving a Data API request in MoEngage, the `customer_id` is used to verify if the user exists in MoEngage. If the user does not exist, a new user is created with the attributes or events.
* The maximum limit for the request body is 128 KB.
* Any string of more than one characters is allowed for `customer_id` except the following values - \['unknown', 'guest', 'null', '0', '1', 'true', 'false', 'user\_attribute\_unique\_id', '(empty)', 'na', 'n/a', '', 'dummy\_seller\_code', 'user\_id', 'id', 'customer\_id', 'uid', 'userid', 'none', '-2', '-1', '2']
The `type` field of each element is case-sensitive and must be lowercase (`customer`, `event`, or `device`). If records are not processed and no user, event, or device is created, verify that the `type` value is lowercase. A value with different casing, such as `Customer`, is not accepted.
For example, a user created using the following request is visible in the dashboard user profile as displayed.
### Sample Request Body
Below is a sample request body for the Create User API:
```json theme={null}
{
"type": "customer",
"customer_id": "USERID1234",
"attributes": {
"first_name":"John",
"name":"John Smith",
"plan_expiry_date":"2020-05-31T00:00:00Z",
"super_user":true,
"user_persona":"browsers",
"platforms" : [{"platform":"ANDROID", "active":"true"}]
}
}
```
You can not use "moe\_" as a prefix while naming events, event attributes, or user attributes. It is a system prefix and using it might result in periodic blacklisting without prior communication.
### Supported Datetime Formats
You can pass the datetime in the following formats in the request body:
| Datetime Format | Example |
| :------------------------------------------- | :------------------- |
| `“datetime_format”:YYYY-MM-DD[T]HH:mm:ss[Z]` | 2019-03-12T17:36:05Z |
| `“datetime_format”: "YYYY-MM-DD"` | 2022-01-22 |
MoEngage performs the following validation on datetime formats before ingesting data into its system:
* Future and past date values are accepted and ingested.
* Any date values with incorrect calendar values (e.g., 2019-15-12 where 15 is not a valid month) are ingested as strings.
* Any datetime values incompatible with the formats mentioned above are converted to strings and then ingested.
## Response
Response to the Data API is a JSON object. On a successful data API request, you will receive the following response:
```json theme={null}
{
"status": "success",
"message": "Your request has been accepted and will be processed soon.",
"request_id": "kXwpDESb"
}
```
On a failed data API request, you will receive the following response:
```json theme={null}
{
"status": "fail",
"error": {
"type": "TypeError",
"message": "expected string",
"request_id": "kXwpDESb"
}
}
```
### Response Codes
The following status codes and associated error messages are returned when the request results in an error.
| Error Code | Type | Message | Description |
| :--------- | :---------------------------- | :--------------------------------------------------------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------- |
| 400 | Missing header value | The Content-Type Header is required | The header value for content type is missing |
| 400 | Empty request body | A valid JSON document is required | The request body is empty |
| 400 | Malformed JSON | Could not decode the request body. The JSON was incorrect or not encoded as UTF-8. | The request JSON is not formed correctly |
| 400 | Blacklisted | Your account is blacklisted, Please contact MoEngage. | Your App is blacklisted in MoEngage |
| 400 | InvalidParams | Given app\_id is invalid. | The App ID is invalid. |
| 400 | ParamsRequired | app\_id is required in path/query params. | App ID is missing in the path or query params. |
| 400 | Empty request body | A valid JSON document is required | The request body is empty. |
| 400 | Body type is not JSON | A valid JSON document is required. | String Payload. |
| 400 | MissingAttributeError | key is expected to be datatype | The specified attributes are invalid |
| 401 | Authentication Required | Authentication Header Required | Authentication header is missing from the request. |
| 401 | Authentication required | No identity information found | Authentication header is empty. |
| 401 | Authentication required | Invalid identity information found | Failure to decode app\_key and app\_secret. |
| 401 | Authentication required | APP\_KEY missing in the authentication header | App\_key is not present in the authentication header. |
| 401 | Authentication required | APP\_SECRET missing in the authentication header | App\_secret is not present in the authentication header. |
| 401 | Authentication required | App Secret key mismatch. Please login to the dashboard to verify key | App secret key is wrong. |
| 401 | Authentication required | Invalid APP\_ID used in Authentication Header | You have used an invalid APP ID in the authentication header. |
| 403 | Account Suspended | Account Suspended | Your account is suspended. |
| 403 | Account Temporarily Suspended | Account Temporarily Suspended | Your account is suspended temporarily. |
| 409 | Authentication Mismatch | App key mismatch in params and authentication | App\_key in parameters and authentication does not match. |
| 409 | Authentication required | App Secret key is not set. Please login to the dashboard to set a key | App Secret not set. |
| 413 | Payload too large | The payload can not exceed 128KB | Request payload size is too large. |
| 415 | Unsupported Media Type | Unsupported Media Type | Unsupported media type. |
| 429 | Rate Limit Exceeded | Rate Limits for User / Event exceeded | You have exceeded the rate limits (number of users or events per minute) defined for your MoEngage account. |
| 5xx | Server Error | Any other exception | This response is returned when the system runs into an unexpected error. We recommend that you retry every 2 seconds for a maximum of 5 times in such cases. |
## Monitor Your Usage
Rate-limited endpoints return the following headers on every response, so your integration can track remaining capacity in real time rather than waiting for a `429`:
```http theme={null}
x-ratelimit-limit: 100
x-ratelimit-remaining: 95
x-ratelimit-reset: 30
```
| Header | Description |
| ----------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `x-ratelimit-limit` | The maximum number of requests allowed within the current 60-second window (w=60). |
| `x-ratelimit-remaining` | The number of requests still available in the current 60-second window before the limit is reached. |
| `x-ratelimit-reset` | The UTC epoch timestamp at which the current window resets. Since the window is 60 seconds (w=60), this value will always be within 60 seconds of the current time. |
A few high-throughput endpoints (such as the Push API) signal throttling with an `x-envoy-ratelimited: true` header instead. Check the specific endpoint's reference page for the headers it returns.
### When You Hit a Limit
When a request is rejected with `HTTP 429`:
1. Stop sending further requests to that endpoint until the window resets (`x-ratelimit-reset`).
2. Retry with exponential backoff rather than retrying immediately.
3. Batch where the endpoint supports it — for example, send multiple user updates in a single Track User request instead of one call per user.
If your integration consistently approaches a limit through legitimate, well-batched usage, contact your CSM or the Support team to request an increase.
## User Attributes
| Key name | Display name | Fetch through Get User API | Update through Track User API | Create through Track User API |
| :-------------------------- | :------------------------------------- | :------------------------- | :---------------------------- | :---------------------------- |
| publisher\_name | Publisher Name | yes | no | yes |
| campaign\_name | Campaign Name | yes | no | yes |
| t\_rev | LTV | yes | no | no |
| t\_trans | No of Conversions | yes | no | no |
| moe\_ip\_city | Last Known City | yes | no | no |
| moe\_ip\_pin | Last Known Pincode | yes | no | no |
| moe\_ip\_subdivision | Last Known State | yes | no | no |
| moe\_ip\_country | Last Known Country | yes | no | no |
| moe\_dtzo | User Timezone Offset (Mins) | yes | no | no |
| u\_s\_c | No. of Sessions | yes | no | no |
| u\_l\_a | Last Seen | yes | no | yes |
| cr\_t | First Seen | yes | no | yes |
| u\_mb | Mobile Number (Standard) | yes | yes | yes |
| uid | ID | yes | yes | yes |
| u\_bd | Birthday | yes | yes | yes |
| u\_em | Email (Standard) | yes | yes | yes |
| locale\_language\_display | Local Language | yes | no | no |
| locale\_country\_display | Local Country | yes | no | no |
| uninstall\_time | Uninstall time | yes | no | no |
| installed | Install Status | yes | no | no |
| moe\_cr\_from | User Creation Source | yes | no | no |
| u\_n | Name | yes | yes | yes |
| u\_ln | Last Name | yes | yes | yes |
| u\_gd | Gender | yes | yes | yes |
| u\_fn | First Name | yes | yes | yes |
| geo | Geolocation | yes | no | no |
| moe\_wa\_subscription | WhatsApp Subscription Status | yes | yes | yes |
| moe\_em\_unsub\_categories | Email Unsubscribed Categories | yes | yes | yes |
| moe\_gaid | Google Advertising ID (Android) | yes | no | yes |
| advertising\_identifier | Advertising Identifier (iOS \&Windows) | yes | no | yes |
| web subscription url | Web Push Subscription Page URL | - | - | - |
| moe\_sub\_w | Web Push Subscription Status | yes | yes | yes |
| moe\_w\_ds | Browser Details | yes | no | no |
| moe\_mweb | Mobile User | yes | no | yes |
| moe\_i\_ov | OS Version iOS | - | - | - |
| moe\_it | Creation Source | no | no | no |
| moe\_spam | Spam | yes | yes | yes |
| moe\_unsubscribe | Unsubscribe | yes | yes | yes |
| moe\_hard\_bounce | Hard Bounce | yes | yes | yes |
| moe\_rsp\_android | Reachability Push Android | yes | no | no |
| moe\_rsp\_ios | Reachability Push iOS | yes | no | no |
| moe\_rsp\_web | Reachability Push Web | yes | no | no |
| moe\_rsu | Reachability Push | yes | no | no |
| moe\_sms\_subscription | SMS Subscription Status | yes | yes | yes |
| moe\_ds\_bts\_push\_hour | Best time to send Push | yes | no | no |
| moe\_ds\_bts\_email\_hour | Best time to Email | yes | no | no |
| moe\_ds\_bts\_sms\_hour | Best time to send SMS | yes | no | no |
| moe\_ds\_mpc\_best\_channel | Most Preferred Channel | yes | no | no |
## Reserved Keywords for User Attributes
Below is the list of keys that should not be used when tracking user attributes.
* USER\_ATTRIBUTE\_UNIQUE\_ID
* USER\_ATTRIBUTE\_USER\_EMAIL
* USER\_ATTRIBUTE\_USER\_MOBILE
* USER\_ATTRIBUTE\_USER\_NAME
* USER\_ATTRIBUTE\_USER\_GENDER
* USER\_ATTRIBUTE\_USER\_FIRST\_NAME
* USER\_ATTRIBUTE\_USER\_LAST\_NAME
* USER\_ATTRIBUTE\_USER\_BDAY
* USER\_ATTRIBUTE\_NOTIFICATION\_PREF
* USER\_ATTRIBUTE\_OLD\_ID
* MOE\_TIME\_FORMAT
* MOE\_TIME\_TIMEZONE
* USER\_ATTRIBUTE\_DND\_START\_TIME
* USER\_ATTRIBUTE\_DND\_END\_TIME
* MOE\_GAID
* INSTALL
* UPDATE
* MOE\_ISLAT
* status
* user\_id
* source
## User Profile Dashboard
On sending data through the data API, it will be populated in the user profile as shown below:
## Limits
The Data API is designed to handle high volumes of data across our customer base. We enforce API limits to ensure responsible use of the API. Refer to the respective endpoint documentation for rate limits.
* If your requirement exceeds the default limits, you can contact the MoEngage support team to increase the limits.
* Make sure to adhere to the Fair Usage Policy (FUP) for high-frequency user data ingestion. This is mandatory to prevent disruption to data processing in your workspace. For more information, refer to the [Fair Usage Policy (FUP)](/docs/user-guide/data/key-concepts/fair-usage-policy-fup).
## FAQs
### Track User
Please attempt exponential backoff of requests to ensure there is no data loss due to 5xx errors.
Getting a 200 status code as a response from MoEngage only indicates that the users in your API payload have been accepted for processing. It does not ensure that the users sent to MoEngage have been successfully ingested. \
Although, this happens very rarely and you can search for newly ingested users in:\
**Segment > Create Segment > Search for users using their IDs**
Please use the [Get User API](/docs/api/user/get-user) to export the users.
Please use the [Delete User API](/docs/api/user/delete-users) to delete existing users in MoEngage.
### Get User
All available users will be found in the `users` key, and users not available will be found in the `users_not_found` key. Please refer to the sample response in this doc.
If the `user_fields_to_export` is not passed, then all custom attributes and exportable standard attributes will be returned. For specific fields, `user_fields_to_export` needs to be passed along with the list of required fields.
### Merge User
No, the merged user will get deleted after calling this API. All the user attributes and devices of the merged user will be transferred to the retained user.
The reachability status of the user will be recalculated based on the devices present after merging the user.
Any registered user present in the MoEngage system can be merged with another registered user irrespective of the source of creation.
Not necessarily; we allow the merging of users with or without devices.
No.
No.
This user will get deleted, and if any devices are attached to this user, they will be associated with the `retained_user`. All events and user details of the `merged_user` will reflect on the `retained_user`. A merge event `MOE_USER_MERGE_EVENT` will be added to the `merged_user` (who will only have the MoEngage ID now).
The `retained_user` will now have all the user, device, and event details of the `merged_user` along with its own existing details. A `MOE_USER_MERGED` event will be added to the `retained_user`.
We will create a new user with that ID, but the MoEngage ID of this user will be different compared to the deleted user.
The user will not be reachable.
The maximum SLA is 30 minutes.
In the user profile, all events of the last 30 days are moved from the merged user to the retained user.
### Delete User
There are no rollback mechanisms for undoing the delete action. Once the delete request is processed, the user is deleted from MoEngage.
No, the events corresponding to a user are not specifically deleted; only the user and the user attributes are deleted when the delete API request is processed.
However, once the user is deleted from MoEngage, the events corresponding to the user will not be accessible. For example, if an event is used in a segmentation query, the deleted user who executed that event will not be added to the calculated segment or campaign.
Navigate to **Segment** -> **Create Segment** on the MoEngage dashboard. Type the unique identifier for the deleted user (ID, MoEngage ID, phone number, email, or any unique identifier you have configured). If the user has been deleted (hard delete), you will not see any search results. For more information about searching users, refer to \[Search User in Segmentation].
### Create Event
Events in the payload need to be mapped to a given user who has executed the event. You must use the Customer ID to identify the events mapped to a customer.
No, anonymous users can be tracked using MoEngage SDKs.
Events in MoEngage are immutable, meaning events can only be created; they cannot be updated or deleted.
### Track Device
No, you can pass only the device attributes mentioned above. Any additional custom attributes passed in the API payload are dropped during processing.
The device will be created based on the Android platform, and the IDFV value passed in the API will be dropped. If the platform is iOS and a GAID value is passed, the device will be created with iOS, but the GAID attribute will be dropped.
You can create a maximum of 1000 devices for a user. The user will be blocked if a 1001st device is created for the user.
In such cases, the existing device will be deleted, and the new device will be added.
### MoEngage Streams
The throughput and average volume of each API request depends on the volume of selected events captured into MoEngage. The default batch size (number of events for each API request) is 100.
The retrial mechanism allows MoEngage to hit your provided endpoints in multiple attempts. In case a batch of events fails (anything other than a "2XX" response from the endpoint is considered failed), the entire batch is retried. MoEngage makes a total of three retry attempts with the following intervals:
* **First Retry:** 30 seconds
* **Second Retry:** 60 seconds
* **Third Retry:** 120 seconds
If you pause Streams, data is not collected for exports and hence cannot be replayed at a later date.
No. Streams is primarily built to export your events in near real-time. Since user attributes in MoEngage are updated asynchronously, it is currently not possible to guarantee the latest values of user attributes in the exports.
As of now, you cannot export data that occurred before configuring Streams. Once configured, you will start seeing data for each event from the moment you enable your exports.
### Bulk Import
Yes, Track User API validations apply to the `Customer` payload type, and Create Event API validations apply to the `Event` payload type within the Bulk API request.
Please implement an **exponential backoff** strategy for your requests. This ensures that your system gradually reduces request frequency during high-load periods, preventing data loss due to server-side errors.
Receiving a **200 OK** status code only indicates that the users in your API payload have been successfully accepted for processing. It does not guarantee that the ingestion process is complete.
While failures are rare, you can verify ingestion by searching for the users in the MoEngage Dashboard: Navigate to **Segment** > **Create Segment** > **Search for users** using their unique IDs.
### Trigger File Imports
No, the schedule still remains the same as originally set up in the MoEngage Dashboard.
## Postman Collection
We have made it easy for you to test the APIs. Click [here](https://www.postman.com/moengage-dev/api-docs/collection/p593wcu/moengage-data-apis) to view the collection in Postman.
# File Import Overview
Source: https://moengage.com/docs/api/data/file-import-overview
Fetch the processing status at both the import and file levels.
The MoEngage File Imports API helps in proactively tracking the status of the Imports and the Files in an import without navigating to the MoEngage dashboard.
## Use Cases
* Fetch the processing status of all imports for a given date range without navigating to the MoEngage dashboard.
* Proactively alert about failures in automated campaigns. For example, if an automated campaign must run using the updated list of users ingested into MoEngage through the import option, but if the import fails for any reason, it may impact the reachability of your campaigns. Typically, you become aware of import failure only when navigating to the MoEngage dashboard or when you receive an email alert. However, with this API, you can track the status proactively even before the campaign is impacted.
## Endpoints
The File Imports API is a collection of the following endpoints:
* [Trigger File Imports API](https://www.moengage.com/docs/api/file-import/trigger-file-imports): Triggers scheduled file imports.
* [Import Details API](https://www.moengage.com/docs/api/file-import/import-details): Fetches the status at an import level.
* [Import File Run History API](https://www.moengage.com/docs/api/file-import/import-file-run-history): Fetches the file processing status of each file contained in an import.
## FAQs
### Trigger File Imports
No, the schedule still remains the same as originally set up in the MoEngage Dashboard.
## Postman Collection
Test these endpoints quickly by importing our Postman collection:
* Trigger File Imports: [**View in Postman**](https://www.postman.com/moengage-dev/api-docs/collection/u3yt8gn/moengage-file-imports-trigger-api?action=share\&creator=3182294)
* Import Details: [**View in Postman**](https://www.postman.com/moengage-dev/api-docs/collection/u3yt8gn/moengage-file-imports-trigger-api?action=share\&creator=3182294)
* Import File Run History: [View in Postman](https://www.postman.com/moengage-dev/api-docs/collection/mzt9mgo/moengage-status-and-run-history-api?action=share\&creator=3182294)
# Streams
Source: https://moengage.com/docs/api/data/moengage-streams
Forward user actions and campaign events to your API endpoint in near real-time using MoEngage Streams.
With MoEngage Streams, you can configure your API endpoint, define the events you want to forward, and then view a stream of data flowing into your system. You can then utilize the data to enrich your data warehouses and recommendation systems.
## Use Cases for MoEngage Streams
The following are a few popular use cases for Streams:
* Enrich the central data warehouse with MoEngage events such as Notification Clicked Android, Email Clicked, SMS Clicked, and so on.
* Forward the Conversion Events, such as Purchase, Song Played, Bill Payment Done, and so on, from MoEngage to the external Analytics tool.
* Feed campaign interaction events from MoEngage to your recommendation system.
* Export the notification interaction data from MoEngage to your machine learning system for optimizing the performance of your algorithms.
* Send all user events present in MoEngage to an external data lake.
## Campaign Interaction Events
The following is a list of Campaign Interaction Events that MoEngage generates, which you will often need to be sent to your API endpoint:
| Event Name | Event Code | Channel |
| :---------------------------- | :------------------------------ | :---------------- |
| Email Sent | `MOE_EMAIL_SENT` | Email |
| Email Deferred | `MOE_EMAIL_DEFERRED` | Email |
| Email Delivered | `MOE_EMAIL_DELIVERED` | Email |
| Email Dropped | `MOE_EMAIL_DROP` | Email |
| Email Bounced | `MOE_EMAIL_HARD_BOUNCE` | Email |
| Email Soft Bounced | `MOE_EMAIL_SOFT_BOUNCE` | Email |
| Email Opened | `MOE_EMAIL_OPEN` | Email |
| Email Clicked | `MOE_EMAIL_CLICK` | Email |
| Email Unsubscribed | `MOE_EMAIL_UNSUBSCRIBE` | Email |
| Email Spam Complained | `MOE_EMAIL_SPAM` | Email |
| SMS Sent | `SMS_SENT` | SMS |
| SMS Delivered | `SMS_DELIVERED` | SMS |
| Notification Received Android | `NOTIFICATION_RECEIVED_MOE` | Push |
| Notification Clicked Android | `NOTIFICATION_CLICKED_MOE` | Push |
| Notification Swiped Android | `NOTIFICATION_CLEARED_MOE` | Push |
| Notification Sent iOS | `n_i_s` | Push |
| Notification Clicked iOS | `NOTIFICATION_CLICKED_IOS_MOE` | Push |
| Notification Received Web | `NOTIFICATION_RECEIVED_WEB_MOE` | Push |
| Notification Clicked Web | `NOTIFICATION_CLICKED_WEB_MOE` | Push |
| Connector Sent | `MOE_CONNECTOR_SENT` | Connector |
| Card Sent | `MOE_CARD_SENT` | Cards |
| Card Delivered | `MOE_CARD_DELIVERED` | Cards |
| Card Viewed | `MOE_CARD_VIEWED` | Cards |
| Card Clicked | `MOE_CARD_CLICKED` | Cards |
| Mobile In-App Shown | `MOE_IN_APP_SHOWN` | Mobile In-Apps |
| Mobile In-App Clicked | `MOE_IN_APP_CLICKED` | Mobile In-Apps |
| Mobile In-App Closed | `MOE_IN_APP_DISMISSED` | Mobile In-Apps |
| On-site Message Shown | `MOE_ONSITE_MESSAGE_SHOWN` | On-site Messaging |
| On-site Message Clicked | `MOE_ONSITE_MESSAGE_CLICKED` | On-site Messaging |
| On-site Message Closed | `MOE_ONSITE_MESSAGE_DISMISSED` | On-site Messaging |
| User Entered Flow | `USER_ENTERED_FLOW` | Flows |
| User Exited Flow | `USER_EXITED_FLOW` | Flows |
| User Added to Control Group | `MOE_CAMPAIGN_CONTROL_GROUP` | All channels |
* With the events above, the following event attributes will be exported by default: `campaign_id`, `campaign_name`, `campaign_type`, `campaign_channel`.
* The following event attributes are not supported for export in streams currently: `email_subject`, `email_click_url`, `inapp_widget_clicked`, `onsite_message_url_clicked`.
For more information on when the campaign events and attributes are tracked, refer to [Data Exports Glossary](/docs/user-guide/data/exports/events/data-export-glossary).
## API Request Format
When Streams sends an event to your API endpoint, the request format will be as below:
**Headers:** `"Content-Type":"application/json"`
**Request Body:**
```json theme={null}
{
"app_name": "App Name",
"source": "MOENGAGE",
"moe_request_id": "moengage unique request id for each request",
"events": [{
"event_name": "Notification Received Android",
"event_code": "NOTIFICATION_RECEIVED_MOE",
"event_uuid": "moengage unique id for each event",
"event_time": 1580967474,
"event_type": "CAMPAIGN_EVENT",
"event_source": "MOENGAGE",
"push_id": "recipient device’s push token",
"uid": "",
"event_attributes": {
"campaign_id": "353df897hkbh67658",
"campaign_name": "Name of the Campaign",
"campaign_type": "Event Trigger",
"campaign_channel": "Push"
},
"user_attributes": {
"moengage_user_id": "moe_internal_user_id",
"user_attr_1": "user_attr_val1",
"user_attr_2": "user_attr_val2"
},
"device_attributes": {
"moengage_device_id": "moe_internal_device_id",
"device_attr_1": "device_attr_val1",
"device_attr_2": "device_attr_val2"
}
}]
}
```
## Streams Data Glossary
For the full list of updated events and attributes, refer to [Data Exports Glossary](/docs/user-guide/data/exports/events/data-export-glossary).
The keys in the API request and their description are:
| Key | Description |
| :------------------- | :----------------------------------------------------------- |
| workspace name | Your workspace name in MoEngage. |
| source | `MoEngage` to identify the requests coming from MoEngage. |
| event\_name | Display Name of the event as seen on the MoEngage dashboard. |
| event\_code | Raw event code as present in the MoEngage system. |
| event\_uuid | Unique event identifier for de-duplication. |
| event\_time | Time of event in UTC (epoch time in seconds). |
| event\_type | `CAMPAIGN_EVENT` or `USER_ACTION_EVENT`. |
| event\_source | Source identifier (value = `MoEngage`). |
| push\_id | Push token (available for Push-related events). |
| email\_id | Recipient email (available for Email-related events). |
| mobile\_number | Recipient mobile number (available for SMS-related events). |
| uid | MoEngage `customer_id` unique identifier. |
| campaign\_name | Campaign name in MoEngage. |
| campaign\_id | Campaign ID in MoEngage. |
| event\_attributes | Dictionary of additional event attributes. |
| campaign\_channel | Channel type: Push / Email / SMS. |
| user\_attributes | Dictionary of additional user properties. |
| moengage\_user\_id | MoEngage internal user ID. |
| device\_attributes | Dictionary of additional device attributes. |
| campaign\_type | Type: Periodic, Active, One-time. |
| variation\_id | Present for campaign variations. |
| locale\_id | Present for campaigns with multiple locales. |
| locale\_name | Display name of the locale. |
| parent\_campaign\_id | Identifier for localized campaign parents. |
| parent\_flow\_id | Identifier for campaigns within a MoEngage Flow. |
| parent\_flow\_name | Name of the MoEngage Flow. |
## Authentication Methods
Streams supports the following types of authentications:
* **No auth**
* **Basic Auth**: Streams supports the standard Basic Auth implementation. You must provide MoEngage with the username and password while configuring Streams. MoEngage always encrypts all authentication data in its systems.
* **OAuth 2.0**: Streams supports OAuth 2.0 authentication using the Client Credentials grant type, where MoEngage fetches and refreshes the access token for you. A self-serve option to select OAuth 2.0 directly in Streams is not yet available. For setup steps, refer to [Configure OAuth 2.0 for Streams](#configure-oauth-20-for-streams).
To support other authentication methods such as API Key, Bearer Token, and so on, you can communicate the same through the enablement ticket and provide MoEngage with the Header key and static value to be passed. Apart from OAuth 2.0, where MoEngage fetches and refreshes the access token through your Authorization configuration, MoEngage cannot support dynamically refreshing API keys and tokens.
## Configure OAuth 2.0 for Streams
To use OAuth 2.0 as the authentication method for Streams, first create an OAuth 2.0 configuration in MoEngage, and then request enablement for your Streams endpoint through a support ticket.
Navigate to **Settings** > **Advanced settings** > **Authorization configuration** and add an OAuth 2.0 configuration using the Client Credentials grant type. Provide the token endpoint, credentials, and response settings for your authorization server. For the full walkthrough, refer to [Authorization Configuration with OAuth 2.0](/docs/user-guide/settings/advanced-settings/authorization-configuration-with-oauth-20).
Save the configuration and verify that its status is **Active** on the Authorization configuration page. A "Failed" configuration cannot fetch a token and cannot be used for Streams.
Open the OAuth 2.0 configuration you created. The configuration ID is the last segment of the page URL. For example, if the URL is `.../advanced/auth-config/edit/69242b6047c75584338719e5`, the configuration ID is `69242b6047c75584338719e5`. Copy this ID to include in the enablement ticket.
Follow the steps in [Enable Streams for Your Account](#enable-streams-for-your-account) and include the OAuth configuration ID so that the Support team can link your OAuth 2.0 configuration to your Streams endpoint.
## Enable Streams for Your Account
Streams is available as part of the Streams add-on. Contact your dedicated MoEngage Customer Success Manager (CSM) to enable it for your account.
Streams can export data to your API endpoint or one of the partners integrated with MoEngage.
### Set Up Streams for Exporting Data to Your API Endpoint
To export data from MoEngage to your servers, contact your MoEngage Customer Success Manager (CSM) or the Support team with the following information:
1. API endpoint to send the data. It can be something like `https://api.example.com/events`.
2. List of events that need to be sent to this API endpoint. A list of campaign events available for export is mentioned in the [Campaign Interaction Events](#campaign-interaction-events) section.
3. List of user attributes and device attributes that need to be sent to the API endpoint with each event.
4. If you need all campaign interaction events, MoEngage exports such events as mentioned in the [Campaign Interaction Events](#campaign-interaction-events) section.
You can use the following template to raise a Streams enablement request:
```text theme={null}
Hey!
Please enable Streams for my Workspace.
Workspace Region: DC-01/DC-02/DC-03/DC-04
Workspace Name: MyWorkspace
Workspace ID:
API Endpoint: [https://www.example.com/api?url_param1=vaue1](https://www.example.com/api?url_param1=vaue1)
Headers:
header1: value1
header2: value2
Authentication:
Method: No auth/Basic Auth/OAuth 2.0
[Only for basic auth] Basic Auth username: "Username"
[Only for basic auth] Basic Auth password: "Password"
[Only for OAuth 2.0] OAuth configuration ID: ""
Events to export: All/List of events
[Optional] List of events: List of events to export
User properties to export: All/List of user properties
[Optional] List of user properties: List of user properties to export
```
**Whitelist IPs**
If your endpoints are in a Virtual Private Cloud (VPC) or not accessible publicly, you must whitelist [these](/docs/user-guide/settings/account/security/ip-whitelisting-in-moengage) MoEngage IPs depending on the region of your Workspace.
### Set Up Streams for Exporting Data to Partners
With Streams, you can export data directly to integrated partners so that you can enrich your marketing/analytics activities on these platforms. As of now, Streams can export data to the following partners:
* [Mixpanel](https://partners.moengage.com/hc/en-us/articles/4410027746836-Mixpanel#01FV1SH74TEQEBE0JVVAWJSWAS)
* [Amplitude](https://partners.moengage.com/hc/en-us/articles/4409507678228-Amplitude-Audiences#h_01HJ11HMQGHRZT4GR260D27QK3)
* [Segment](https://partners.moengage.com/hc/en-us/articles/12661517099284-MoEngage-Source)
* [mParticle](https://partners.moengage.com/hc/en-us/articles/10223363585428-mParticle-Inbound)
* [Rudderstack](https://partners.moengage.com/hc/en-us/articles/12718141249556-MoEngage-Source)
Please follow partner-specific integration documents for setting this up.
## Stream Events to Another MoEngage App
With MoEngage Streams, you can sync events from one MoEngage App to another. Common use cases include:
* **Cross-Product Promotions**: If you have teams across various product offerings, keeping two apps synced helps you cross-promote products to upsell and increase conversion.
* **Centralized Analysis**: Businesses with multiple apps can run cross-app analysis to unlock higher LTV, cross-sell, and increase brand loyalty.
To set this up:
Speak to your CSM to get your dedicated Streams endpoint for your destination app.
Follow the [enablement steps](#enable-streams-for-your-account) to configure the source app with the following details:
* **Endpoint:** Use the dedicated MoEngage endpoint provided for your destination app.
* **Authentication:** Enable Basic Auth. Use your destination app's **Workspace ID** as the username and its **Data API Key** as the password.
* **Header:** Provide `"Content-Type" : "application/json"`.
* **Events/Properties:** Select the list of events and user properties you want to stream.
## Limitations
Please be aware of the following constraints or limitations while integrating Streams with third-party apps.
* MoEngage can send data to only one static endpoint. If the access token expires, use an OAuth 2.0 configuration so that MoEngage can fetch and refresh the token for you (see [Authentication Methods](#authentication-methods)). For any other authentication method, MoEngage cannot integrate with an endpoint where the access token or link expires or is refreshed every few hours.
* MoEngage cannot modify the body or key names while streaming the data.
## Frequently Asked Questions
The throughput and average volume of each API request depends on the volume of selected events captured into MoEngage. The default batch size (number of events for each API request) is 100.
In case a batch of events fail (any non-2XX response), the entire batch is retried. In total, three retry attempts are made with intervals of 30, 60, and 120 seconds.
No. If you pause Streams, data is not collected for exports and cannot be replayed at a later date.
No. Streams is built for near real-time event export. Since user attributes are updated asynchronously, MoEngage cannot guarantee the latest values in the export.
No. You can only export data generated after the Streams configuration is enabled.
# Device Opt-out
Source: https://moengage.com/docs/api/device/device-opt-out
/api/data/data.yaml post /devices/manage
This API blocks or unblocks specific devices from receiving push notifications triggered from MoEngage. To prevent push notifications from reaching a specific user or all devices associated with a user, call the API to block them. This is useful for reasons such as device theft or fraudulent activity. For example, if a device is stolen, you can use this API to block it in MoEngage, ensuring that any scheduled push notifications with sensitive content do not get delivered. You can also use the API to unblock a device if it is recovered.
#### Rate Limit
The rate limit is 1000 API requests per minute.
# Track Device
Source: https://moengage.com/docs/api/device/track-device
/api/data/data.yaml post /device/{app_id}
This API adds or updates devices and device properties in MoEngage. You can create a new device for an existing user, create new device properties for an existing user, or update the device properties of the existing user.
#### Rate Limit
A single API request contains one or more device updates. Maintain a rate limit of 10,000 device updates per minute.
# Email Subscription Overview
Source: https://moengage.com/docs/api/email-subscription/email-subscription-overview
Manage user email resubscription, opt-in status, and category preferences.
The MoEngage Email Subscription Management APIs allow you to update email preferences for your users directly within the MoEngage platform. This suite enables you to synchronize resubscription status with external Email Service Providers (ESPs) and manage granular category-level opt-ins to ensure compliance and a better user experience.
## Endpoints
The Email Subscription API consists of the following endpoints:
* [Bulk Resubscribe Users](/docs/api/resubscribe/bulk-resubscribe-users): Resubscribes users and optionally updates ESP suppression lists.
* [Update User Email Opt-in Preferences](/docs/api/opt-in-management/update-user-email-opt-in-preferences): Updates overall opt-in status and category-specific preferences.
## API Capabilities
| Feature | Bulk Resubscribe | Opt-in Management |
| ----------------------- | ---------------------- | ----------------- |
| **Sync with ESP** | ✓ (SendGrid supported) | ✗ |
| **Category Management** | ✗ | ✓ |
| **Bulk Processing** | ✓ (Up to 100 users) | ✗ (Single User) |
| **Async Processing** | ✓ | ✗ |
## Implementation Notes
**ESP Support:**
The Resubscription API currently supports **SendGrid** only. When `update_esp` is set to `true`, MoEngage automatically removes the email addresses from your SendGrid suppression list.
**Identity Management for Opt-in:**
* **PII Tokenization:** You must pass the `customer_id`. MoEngage uses this to locate the user as the email ID is not stored.
* **PII Encryption:** Pass either the `customer_id` or the decrypted `email_id`. If only the email ID is passed, all users associated with that email will be updated.
## FAQs
### Resubscription
We will automatically call the ESP to remove the recipient email addresses sent in the request from their suppression list.
This attribute is configured in the User Attribute that stores user’s email address field in the General Settings for the Email channel. You can either use the “Email Standard” attribute that MoEngage uses by default or configure any other custom attribute of your choice to store the email address of your users. This attribute is available in the User Profile for each user.
The Resubscription API will use this attribute to look for the users mapped to the email addresses in the recipient list and update their subscription status.
The Current Connector field in Email -> General Settings has the information about which ESP is used. The Resubscription API will use this information to update the suppression list in the ESP.
If any other ESP is sent in the request or configured as the default connector, then only the Unsubscribe standard attribute in MoEngage will be updated to false, and the ESP will not be updated (only Sendgrid ESP is supported for the Resubscription API).
When the ESP is changed in the Email General Settings, it takes up to 15 minutes for the settings to get reflected. If a resubscription request is executed within this time frame, the older settings will get picked. For example, if the ESP is changed from Amazon to SendGrid and a resubscription request is performed within 15 minutes of updating the settings, the ESP would still be Amazon. The unsubscribe flag will get updated in the MoEngage Dashboard alone.
The Data API updates only the unsubscribe attribute in MoEngage. The Resubscribe Email API additionally syncs with your ESP (SendGrid) to remove the email from their suppression list.
### Opt-in and Categories
The user attribute specified in the General Settings of email will be used to search for users matching the provided email ID.
Email ID is given higher priority, and MoEngage finds all users with the same email ID and updates them with the passed opt-in status value. However, if you have requested not to carry forward the opt-in status or unsubscribe information across profiles with the same email ID, MoEngage picks the customer\_id and uses it to update the opt-in status.
Yes, they are carried forward by default. If User A and B have the same email ID and MoEngage received consent from A, both A and B are marked as double-opted in. This can always be controlled. To do so, contact the MoEngage team.
If the API payload has even one category that is not active or present in your workspace, MoEngage throws an error and drops the request.
No, it is not mandatory. If required, you can always track this at the global level.
Suppose API key rotation is practiced, and the workspace has more than one active API key for a smoother transition. In that case, you can use any active API key associated with Email Double optin for authorization purposes.
Whenever a user's opt-in status is updated using this API, the Resubscribe API, the Update Subscription Preference API, or via the end-user interaction, MoEngage raises an event called Email Optin Status Updated with the necessary information.
* If you use PII Tokenization for your emails, ensure you pass the user ID when calling the Email Opt-in Management API. MoEngage uses this user ID to locate the user and update their opt-in status. Passing the email ID is ineffective in this case, as MoEngage does not store it.
* If you use PII Encryption to send emails, ensure you pass either the user ID or the decrypted email ID. MoEngage uses this information to locate the associated user and update their opt-in status, or to update the opt-in status of all users having that email ID.
## Postman Collections
Test these subscription APIs immediately using our Postman collection. View Postman Collection → [**Resubscribe API**](https://www.postman.com/moengage-dev/api-docs/collection/xt2ptnc/moengage-resubscribe-api) and [**Email Optin Management**](https://www.postman.com/moengage-dev/api-docs/request/fcc8l9m/email-optin-managment?tab=overview).
# Email Templates (V1) Overview
Source: https://moengage.com/docs/api/email-templates-1/email-templates-1-overview
Define, reuse, update, and manage email templates created outside the MoEngage ecosystem effortlessly.
The MoEngage Email Template API allows marketers to define, reuse, update, and manage email templates created outside the MoEngage ecosystem with ease. The templates created and updated using these APIs are supported for the Custom HTML Editor (Froala Editor) and not the Drag and Drop Editor.
## Endpoints
The Email Template API (V1) is a collection of the following endpoints:
* [Create Email Template](/docs/api/email-templates/create-email-template-v1): Creates an email template.
* [Update a Specific Email Template](/docs/api/email-templates/update-specific-template): Updates an email template specified by its Template ID.
* [Bulk Create/Update Email Template](/docs/api/email-templates/bulk-createupdate-templates): Creates and updates email templates in bulk.
* [Get a Specific Email Template](/docs/api/email-templates/get-specific-template): Fetches an email template using its Template ID.
* [Get all Email Templates](/docs/api/email-templates/get-all-templates): Fetches all the email templates in your MoEngage account.
## FAQs
### Create Email Template
You can access templates created using the Create Email Template API in the My Saved Templates tab during the second step of campaign creation in the MoEngage Dashboard, as illustrated in the image below:
### Update a Specific Email Template
The update request will overwrite the existing template with the template shared in the update request. If the update request does not have some of the fields that the created template had, the updated template will not have them too. For example, if you do not pass any attachments in the update request, the updated template will not have any attachments, even if it had attachments while it was created.
### Bulk Create/Update Email Template
In the case of an update, the template to be updated is identified using its template id. It is mandatory to pass the id field in the required for an update template request. Otherwise, it will be treated as a create template request.
### Get a Specific Email Template
The error description contains the following message in such a case: “*Template for given template id is archived*”.
## Postman Collection
Test these endpoints quickly by importing our Postman collection: [**View in Postman**](https://www.postman.com/moengage-dev/api-docs/collection/dc04rwi/moengage-content-email-template-apis-v2?action=share\&creator=3182294)
# Email Templates (V2) Overview
Source: https://moengage.com/docs/api/email-templates-2/email-templates-2-overview
Define, reuse, update, and manage email templates created outside the MoEngage ecosystem effortlessly.
The MoEngage Email Template API allows marketers define, reuse, update, and manage email templates created outside the MoEngage ecosystem effortlessly. You can create multiple versions of the same template and mark whether they can be used in the campaigns that are active currently. Users can create templates using the Create Email Template API and update them using the Update Email Template API or edit them in the MoEngage dashboard (provided they have the specific editing permissions for the templates allowed for their role). The templates created and updated using these APIs are supported for the Custom HTML Editor (Froala Editor) and not the Drag and Drop Editor.
## Endpoints
The Email Template API (V2) is a collection of the following endpoints:
* [Create Email Template API](/docs/api/email-templates/create-email-template-v2): Creates an email template.
* [Update Email Template API](/docs/api/email-templates/update-email-template): Updates an email template specified by its Template ID.
* [Search Email Template API](/docs/api/email-templates/search-email-template): Fetches an email template using its Template ID or other filters like template name, template version, and so on. It can also list all the email templates created in MoEngage using the Create Email Template API.
## FAQs
### Create Email Template API
You can access templates created using the Create Email Template API in the Imported API Templates tab in the second step of campaign creation in the MoEngage Dashboard, as illustrated in the image below:
Yes, you can create multiple templates with the same name, provided they have different versions.
# Bulk Create/Update Templates
Source: https://moengage.com/docs/api/email-templates/bulk-createupdate-templates
/api/email-templates-1/email-templates-1.yaml put /bulk/email-templates
This API creates or updates email templates in bulk. You can create or update up to 50 templates in a single request.
#### Rate Limit
The rate limits are at the workspace level, and a maximum of 1000 (sum of all the API requests per workspace) requests are allowed for a workspace per minute.
# Create Email Template (V1)
Source: https://moengage.com/docs/api/email-templates/create-email-template-v1
/api/email-templates-1/email-templates-1.yaml post /email-templates
This API creates an email template in MoEngage. You can use this API to upload templates created outside the MoEngage ecosystem to MoEngage and use them for campaign creation on the MoEngage dashboard.
#### Rate Limit
The rate limits are at the workspace level, and a maximum of 1000 (sum of all the API requests per workspace) requests are allowed for a workspace per minute.
# Create Email Template (V2)
Source: https://moengage.com/docs/api/email-templates/create-email-template-v2
/api/email-templates-2/email-templates-2.yaml post /custom-templates/email
This API creates an email template in MoEngage. You can use this API to upload templates created outside the MoEngage ecosystem to MoEngage and use them for campaign creation. The uploaded templates can be edited in the Froala editor (custom HTML editor) on the MoEngage dashboard.
#### Rate Limit
The rate limit is 100 RPM. You can upload a maximum of 10000 templates per channel.
# Get All Templates
Source: https://moengage.com/docs/api/email-templates/get-all-templates
/api/email-templates-1/email-templates-1.yaml get /email-templates
This API fetches the list of all the email templates available in your MoEngage account.
#### Rate Limit
The rate limits are at the workspace level, and a maximum of 1000 (sum of all the API requests per workspace) requests are allowed for a workspace per minute.
# Get Specific Template
Source: https://moengage.com/docs/api/email-templates/get-specific-template
/api/email-templates-1/email-templates-1.yaml get /email-templates/{id}
This API fetches an email template using its template ID.
#### Rate Limit
The rate limits are at the workspace level, and a maximum of 1000 (sum of all the API requests per workspace) requests are allowed for a workspace per minute.
# Search Email Template
Source: https://moengage.com/docs/api/email-templates/search-email-template
/api/email-templates-2/email-templates-2.yaml post /custom-templates/email/search
This API searches the email templates created in your MoEngage account.
**Note**
We are introducing mandatory pagination, effective November 15, 2025, all calls to this API must include the following two parameters:
* `page`: The page number of the results you wish to fetch.
* `entries`: The number of templates to return per page, with a maximum value of 15.
Please update all integrations to include these parameters. API requests submitted without them after the effective date will result in an error and fail to execute.
#### Rate Limit
The rate limit is 100 Requests Per Minute.
# Update Email Template
Source: https://moengage.com/docs/api/email-templates/update-email-template
/api/email-templates-2/email-templates-2.yaml put /custom-templates/email
This API updates an email template by specifying its external template ID. You can specify whether the updated version of the template can be used in active campaigns in the request.
#### Rate Limit
The rate limit is 100 Requests Per Minute.
# Update Specific Template
Source: https://moengage.com/docs/api/email-templates/update-specific-template
/api/email-templates-1/email-templates-1.yaml put /email-templates/{id}
Updates an existing email template by specifying its template ID in the path.
#### Rate Limit
The rate limits are at the workspace level, and a maximum of 1000 (sum of all the API requests per workspace) requests are allowed for a workspace per minute.
# Track Event
Source: https://moengage.com/docs/api/event/track-event
/api/data/data.yaml post /event/{Workspace_ID}
This API tracks the actions of a user.
* If you have [Portfolio](/docs/user-guide/settings/account/portfolio/portfolio) enabled for your workspace, you need to pass project\_code in the API endpoint. This identifies which project a user or event belongs to. For more information, refer to [Portfolio: Data Ingestion and Management](/docs/user-guide/data/key-concepts/portfolio-data-ingestion-and-management).
* MoEngage does not accept any future dated events.
#### Rate Limit
A single API request contains one or more events. Maintain a rate limit of 30,000 events per minute.
# Track Experience Events
Source: https://moengage.com/docs/api/events/track-experience-events
/api/personalize-experience/personalize-experience.yaml post /experiences/events
This API tracks impressions (shown) and user interactions (clicked) for accurate experience reporting. To report an impression or click for your experience via API, use the following endpoint.
If you do not call this endpoint, your MoEngage analytics dashboard will show zero impressions and zero clicks, and campaign reporting will be empty.
# Fetch Experience
Source: https://moengage.com/docs/api/experiences/fetch-experience
/api/personalize-experience/personalize-experience.yaml post /experiences/fetch
This API receives data on active personalization experiences. You can fetch data for one or more server-side experiences by using the **experience_key** field. MoEngage will evaluate targeting rules and in-session attributes automatically and return the correct variation for the user. Typically, you would make this call as part of your larger page and content rendering pipeline.
#### Rate Limit
The rate limit is **10,000 RPM** (requests per minute), applicable at the workspace (App) level. This limit is configurable on request and may incur additional cost.
# Fetch Experience Metadata
Source: https://moengage.com/docs/api/experiences/fetch-experience-metadata
/api/personalize-experience/personalize-experience.yaml get /experiences/metadata
This API fetches a list of currently Active, Scheduled, and Paused experiences within a workspace.
**Recommended usage**: Call this endpoint once at application startup and cache the result. Experience keys change infrequently — a daily refresh is usually sufficient. In production rendering paths, filter by `?status=Active` so your [Fetch Experience](/docs/api/experiences/fetch-experience) calls only include keys that are currently live.
# Import Details
Source: https://moengage.com/docs/api/file-import/import-details
/api/data/data.yaml post /fileimports/import/status
This API fetches the status at an import level. It can fetch the status of multiple imports but not the status of the files within the import.
#### Rate Limit
You can create 50 requests per minute.
# Import File Run History
Source: https://moengage.com/docs/api/file-import/import-file-run-history
/api/data/data.yaml post /fileimports/import/run/history
This API fetches the file processing status of each file contained in an import. The API request must contain either the import_name or the import_id. If you are not sure of the import_id or the import_name, you can use the [Import Details API](https://www.moengage.com/docs/api/file-import/import-details) to get the import details, which can be further used in the Import File Run History API.
#### Rate Limit
You can create 50 requests per minute.
# Trigger File Imports
Source: https://moengage.com/docs/api/file-import/trigger-file-imports
/api/data/data.yaml post /fileimports/trigger/{schedule_id}
This API triggers scheduled file imports. You can trigger periodic imports to run using this API if the import has not expired and is in any of the following states- Scheduled, Successful, Partially Successful, and Failed.
#### Rate Limit
You can trigger this API once in every five minutes for a specific schedule\_id. A Bad request response (400) response will be sent if this is exceeded.
# Add Users to File Segment
Source: https://moengage.com/docs/api/file-segments/add-users-to-file-segment
/api/custom-segments/custom-segments.yaml put /v2/custom-segments/file-segment/add-users
This API adds a list of users from a CSV file to an existing file segment.
This API endpoint does not currently support Team-level scoping. All segments generated using this call will be assigned to the Default Team automatically.
# Create File Segment
Source: https://moengage.com/docs/api/file-segments/create-file-segment
/api/custom-segments/custom-segments.yaml post /v2/custom-segments/file-segment
This API creates a new file segment from a CSV file URL.
* If your file is private, you should whitelist [these IPs](/docs/user-guide/settings/account/security/ip-whitelisting-in-moengage) to provide access only to MoEngage for the file.
* This API endpoint does not currently support Team-level scoping. All segments generated using this call will be assigned to the Default Team automatically.
#### Rate Limits
| Rate Limit Name | Rate Limit |
| :-------------------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------- |
| total active segment | The limit of the total number of active segments at a time for a client is 1000. |
| file\_segment ops per hour | The total number of file segment operations (create/add/remove) per hour per client allowed is 10. |
| file\_segment ops per day | The total number of file segment operations (create/add/remove) per day per client allowed is 100. |
| file\_segment users per day | The total number of users uploaded via the File segment is limited to 2 million per day. (This limit is customizable, contact the MoEngage Support team). |
| file\_size\_limit | The size of the file from which the segment is created/updated. For each request, the file size limit is 150 MB. |
**Notes**
* Breaching the limits will reject the request.
* Per hour and per day limits will consider the calculation based on the last hour and last 24 hrs respectively.
* The limit of 1000 active segments is calculated across all types of 'active segments'. Most of our customers utilise only 30-40% of this limit at any given point.
#### CSV File Requirements
* The attribute names should be separated by new lines.
* CSV should be a single column and less than 150MB.
* Values should not end with a comma (e.g., `abcd` not `abcd,`).
* Values should not have duplicates or special characters (e.g., `abcd` not `"abcd"` or `a#bc`).
* File should not have empty rows or columns.
* A user attribute value must uniquely identify a single user.
* [Sample File Link](https://app-cdn.moengage.com/assets/Sample_GAIDs.csv)
#### Segment Processing and Availability
As soon as the request is received at the MoEngage system, MoEngage creates a segment with zero users. After this, the file is downloaded, processed, and users are added to the segment. If the segment is queried during processing, it will show zero or partial user count.
There is no fixed processing timeout. If the initial file download fails, MoEngage automatically retries before reporting a failure via the callback.
#### Callback Payload
When file processing completes, MoEngage sends a `POST` request to your `callback_url`. Your server must return an HTTP `200` to acknowledge receipt.
The payload structure depends on the processing outcome.
**Success (status: 201)**
| Field | Type | Description |
| :----------------- | :------ | :----------------------------------------------------------------------------------------------- |
| `db_name` | string | The MoEngage database name for your workspace. |
| `segment_name` | string | The name of the processed segment. |
| `request_id` | string | Unique identifier for this processing request. |
| `status` | integer | `201` on successful processing. |
| `values_found` | integer | Number of rows present in the uploaded file. |
| `values_processed` | integer | Number of values processed from `values_found`. Values with corrupted or empty data are skipped. |
| `user_count` | integer | Number of users found in MoEngage from the processed values and added to the segment. |
**Failure (status: 400 or 500)**
| Field | Type | Description |
| :-------------- | :------ | :----------------------------------------------------------------------------------------------- |
| `db_name` | string | The MoEngage database name for your workspace. |
| `segment_name` | string | The name of the segment for which processing failed. |
| `request_id` | string | Unique identifier for this processing request. |
| `status` | integer | `400` for client errors (for example, file too large, download failed), `500` for server errors. |
| `error_message` | string | Description of what caused the processing to fail. |
# Remove Users from File Segment
Source: https://moengage.com/docs/api/file-segments/remove-users-from-file-segment
/api/custom-segments/custom-segments.yaml put /v2/custom-segments/file-segment/remove-users
This API removes a list of users from a CSV file from an existing file segment.
This API endpoint does not currently support Team-level scoping. All segments generated using this call will be assigned to the Default Team automatically.
# Replace Users from File Segment
Source: https://moengage.com/docs/api/file-segments/replace-users-from-file-segment
/api/custom-segments/custom-segments.yaml put /v2/custom-segments/file-segment/replace
This API replaces all users in an existing file segment with a new list of users from a CSV file.
**Notes:**
* This API drops all existing users from the segment and adds the new users provided in the File URL.
* Only the newly added users are counted towards the daily file segment user limit.
* This API does not currently support Team-level scoping. All segments generated using this call will be assigned to the Default Team automatically.
# Create Filter Segment
Source: https://moengage.com/docs/api/filter-segments/create-filter-segment
/api/custom-segments/custom-segments.yaml post /v3/custom-segments
This API creates a new segment based on a set of filter conditions.
This API endpoint does not currently support Team-level scoping. All segments generated using this call will be assigned to the Default Team automatically.
#### Generate Request from Dashboard
To simplify payload generation, MoEngage provides a tool in the dashboard where you can configure filters and export the payload.
1. Log in to the MoEngage dashboard.
2. Click **Test & Debug** at the lower left in the side panel.
3. Click **Segment Payload**.
4. Specify the segment name and configure the required filters.
5. Click **Generate Payload**.
#### Rate Limit
The rate limit is 50 requests/minute, 200 requests/hour, and 1000 requests/day.
***
## Payload Reference
### Top-Level Structure
Every segment request is a tree of filters under `included_filters` and an optional `excluded_filters` root.
```json
{
"name": "my-segment",
"included_filters": {
"filter_operator": "and",
"filters": []
},
"excluded_filters": {
"filter_operator": "and",
"filters": []
}
}
```
`filter_operator` at every level accepts `"and"` or `"or"`. Groups can nest to arbitrary depth using `nested_filters`.
***
### Filter Types
| `filter_type` | Purpose |
| --------------------- | ---------------------------------------------------- |
| `user_attributes` | Filter by a user profile attribute |
| `actions` | Filter by an event the user has or has not performed |
| `psychographic_event` | Filter by affinity/behavioral patterns over an event |
| `custom_segments` | Reference a saved segment by ID |
| `nested_filters` | AND/OR group container for combining other filters |
***
### User Property Filter (`filter_type: "user_attributes"`)
#### Required Fields
| Field | Type | Notes |
| ------------- | ------- | ------------------------------------------------------------------------------------------------------------------ |
| `filter_type` | string | Always `"user_attributes"` |
| `name` | string | Attribute name, for example `last_purchase_date` |
| `data_type` | string | See data type table below |
| `category` | string | The attribute group the attribute belongs to (for example, `"Tracked Custom Attribute"`). Always include this key. |
| `operator` | string | Allowed set varies by `data_type`; omit for `geopoint`, `object`, `array_object` |
| `negate` | boolean | `true` inverts the filter; omitted for `object` and `array_object` |
| `value` | varies | Shape depends on operator; absent for `exists` and `today` |
In portfolio (multi-project) workspaces, User Property filters also accept `project_name`. See Portfolio Workspaces under the User Behavior filter for the values it takes.
#### Supported Data Types
| `data_type` | Allowed operators | Extra fields |
| -------------- | ------------------------------------------------------------------------------------------------------------------------------------------ | -------------------------------------------------------------------------------------- |
| `string` | `in`, `is`, `contains`, `startsWith`, `endsWith`, `containsInTheFollowing`, `startsWithInTheFollowing`, `endsWithInTheFollowing`, `exists` | `case_sensitive` |
| `double` | `in`, `lessThan`, `greaterThan`, `between`, `exists` | `value1` when `operator` is `between` |
| `bool` | `is`, `exists` | — |
| `datetime` | `on`, `between`, `before`, `after`, `inTheLast`, `inTheNext`, `today`, `is`, `in`, `exists` | `value_type`, `value1` for `between`, `extract_type` for date-part filters |
| `geopoint` | (implicit `around` — no `operator`) | `value` (latitude), `value1` (longitude), `radius`; carries `negate` but no `operator` |
| `array_string` | `in`, `contains`, `startsWith`, `endsWith`, `is`, `exists` | `case_sensitive`, `array_filter_type` (`any_of` or `all_of`) |
| `array_double` | `in`, `lessThan`, `greaterThan`, `between`, `exists` | `array_filter_type` (`any_of` or `all_of`); `value1` for `between` |
| `object` | N/A | `filter_operator`, `filters[]` (recursive); no `operator`, `negate`, or `value` |
| `array_object` | N/A | `filter_operator`, `filters[]` (recursive); no `operator`, `negate`, or `value` |
**"Contains spaces" and "is empty":** the dashboard shows these as operators, but there is no `containsSpaces` or `is_empty` operator at the payload level. Send the equivalent operator and value instead:
| Dashboard option | `operator` | `value` |
| ---------------- | ---------- | ---------------------- |
| Contains spaces | `contains` | `" "` (a single space) |
| Is empty | `is` | `""` (an empty string) |
Negate either one with `negate: true` to express "does not contain spaces" or "is not empty".
**`value` shape by operator:**
* Array: `in` (string, double, array types); `containsInTheFollowing`, `startsWithInTheFollowing`, `endsWithInTheFollowing` (string); `contains`, `startsWith`, `endsWith` (array\_string); `in` (datetime with time/day/month extract types)
* Scalar: `is` (bool, datetime, and the string/array\_string cases in the table above), `on`, `before`, `after`, `lessThan`, `greaterThan`, `inTheLast`, `inTheNext`
* Absent: `exists`, `today`
**`exists` cleans up:** When `operator` is `exists`, `value`, `value1`, and `value_type` are removed from the payload.
#### Datetime `value_type` and `extract_type`
Set `value_type` to:
* `"absolute"` — `value` is an ISO 8601 date string, for example `"2024-01-15T00:00:00.000Z"`
* `"relative_past"` — `value` is an integer number of days/hours/months ago
* `"relative_future"` — `value` is an integer number of days/hours/months in the future (used with the `after` and `inTheNext` operators)
Set `extract_type` to filter on a specific part of the date. Omit it to match on the full date:
* `"time_of_the_day"` — hour (0–23)
* `"day_of_the_week"` — weekday (0–6)
* `"day_of_the_month"` — day (1–31)
* `"month_of_the_year"` — month (1–12)
* `"date_month_of_the_year"` — month and day as an `MM-DD` string, for example `"06-15"`
#### Cross-Attribute Comparison (Dynamic Values)
To compare a user attribute against another user attribute rather than a literal, set `is_dynamic_value: true`, set `dynamic_attribute_type` to the base type of the referenced attribute, and use a template string in `value`:
| Source | `value` template |
| -------------- | --------------------------------------- |
| User attribute | `"{{MoeUserAttribute['']}}"` |
Segment creation supports comparing a user attribute against another **user attribute** only. Comparing against an event attribute or a business event attribute is available in campaign filters, not when creating a segment.
For array types, `dynamic_attribute_type` uses the base element type: `array_string` → `"string"`, `array_double` → `"double"`. For `datetime`, `value_type` is forced to `"absolute"`.
Cross-attribute comparison is not available for:
* `object` and `array_object` attributes.
* `double` and `array_double` attributes when `operator` is `between`.
```json
{
"name": "order_value",
"data_type": "double",
"filter_type": "user_attributes",
"operator": "greaterThan",
"negate": false,
"value": "{{MoeUserAttribute['lifetime_value']}}",
"is_dynamic_value": true,
"dynamic_attribute_type": "double"
}
```
***
### User Behavior Filter (`filter_type: "actions"`)
#### Required Fields
| Field | Type | Notes |
| -------------------- | ------- | ------------------------------------------------------------------------------------ |
| `filter_type` | string | Always `"actions"` |
| `action_name` | string | The internal event name |
| `project_name` | string | Optional; portfolio (multi-project) workspaces only. See Portfolio Workspaces below. |
| `executed` | boolean | `true` = has performed; `false` = has NOT performed |
| `execution` | object | Frequency condition |
| `primary_time_range` | object | Time window for the event |
| `attributes` | object | Event attribute sub-filters; always include, even when empty |
The three object fields use these key names:
```json
{
"filter_type": "actions",
"action_name": "purchase",
"executed": true,
"execution": { "type": "atleast", "count": 1 },
"primary_time_range": {
"type": "inTheLast",
"value": 30,
"value_type": "relative_past",
"period_unit": "days"
},
"attributes": { "filter_operator": "and", "filters": [] }
}
```
#### Execution (Frequency)
| `execution.type` | Meaning | `count` required |
| ---------------- | ----------------------------------------------- | ----------------- |
| `atleast` | At least N times (default for `executed: true`) | Yes |
| `exactly` | Exactly N times | Yes |
| `atmost` | At most N times | Yes |
| `firstTime` | For the first time only | No — omit `count` |
| `lastTime` | For the last time only | No — omit `count` |
When `executed: false`, `execution` is `{ "type": "exactly", "count": 0 }`.
#### Primary Time Range
`primary_time_range` stores five keys: `type`, `value`, `value1` (only for `between`), `value_type`, and `period_unit`. The payload accepts five `type` values:
| `type` | `value` shape | Needs `value1` | `value_type` |
| ----------- | -------------------------------------- | --------------- | ----------------------------- |
| `inTheLast` | Integer count of `period_unit` | No | `relative_past` (locked) |
| `between` | Start value: ISO date or integer count | Yes (end value) | `absolute` or `relative_past` |
| `on` | ISO date or integer count | No | `absolute` or `relative_past` |
| `before` | ISO date or integer count | No | `absolute` or `relative_past` |
| `after` | ISO 8601 date | No | `absolute` (locked) |
`period_unit` accepts `hours`, `days`, `weeks`, or `months`, and applies to `inTheLast`.
The dashboard also offers calendar windows such as **Today** and **This week**. These are not payload `type` values — each maps onto `type: "on"` with a specific `value` and `period_unit`:
| Dashboard option | Payload |
| ---------------- | -------------------------------------------------------------------------------------- |
| Today | `{ "type": "on", "value": 0, "value_type": "relative_past", "period_unit": "days" }` |
| Yesterday | `{ "type": "on", "value": 1, "value_type": "relative_past", "period_unit": "days" }` |
| This week | `{ "type": "on", "value": 0, "value_type": "relative_past", "period_unit": "weeks" }` |
| Last week | `{ "type": "on", "value": 1, "value_type": "relative_past", "period_unit": "weeks" }` |
| This month | `{ "type": "on", "value": 0, "value_type": "relative_past", "period_unit": "months" }` |
| Last month | `{ "type": "on", "value": 1, "value_type": "relative_past", "period_unit": "months" }` |
Absolute date formatting: `value` → `YYYY-MM-DDT00:00:00.000Z`; `value1` → `YYYY-MM-DDT23:59:59.999Z`. For `between`, `value1` must be greater than `value`.
Segments built in the dashboard store relative windows as `days` (and `days1`) instead of `value` and `period_unit`. A response for one of those segments returns that form. Send `value`, `value_type`, and `period_unit` when you create or update a segment through the API.
#### Event Attribute Sub-Filters
Use the `attributes` block to narrow which event occurrences count — for example, a `purchase` event where `product_category` is `"electronics"`.
Inner filters follow the same shape as User Property filters, with two differences:
* `filter_type` is `"action_attributes"` instead of `"user_attributes"`.
* `category` is `"default"`.
The API omits `filter_type` when it echoes these filters back in a response, so send it on the request even though a `GET` on the segment will not show it.
Inner filters start directly from `filter_operator` and `filters` — do not add `included_filters` or `excluded_filters` inside the `attributes` block.
```json
"attributes": {
"filter_operator": "and",
"filters": [
{
"name": "currency",
"data_type": "string",
"category": "default",
"filter_type": "action_attributes",
"operator": "is",
"negate": false,
"case_sensitive": false,
"value": "USD"
}
]
}
```
#### Aggregation (sum / avg / min / max / median)
Add `aggregation_attributes` to compare an aggregate of a numeric event attribute against a threshold. The block holds exactly one filter.
Aggregation is only available when `executed: true`, `execution.type` is not `firstTime` or `lastTime`, and `primary_time_range.type` is not `before` or `after`.
| Key | Values |
| ------------------ | ------------------------------------------------------------------------------------------------------ |
| `attribute_name` | The numeric event attribute to aggregate |
| `data_type` | `double` |
| `aggregation_type` | `sum`, `avg`, `min`, `max`, `median` |
| `operator` | `is`, `between`, `lessThan`, `greaterThan` |
| `negate` | `true` inverts the comparison ("is not equal to", "is not between") |
| `value` | The numeric threshold; add `value1` when `operator` is `between` |
| `comparator` | Omit for a plain aggregate. Set to `change` or `percentageChange` to compare against an earlier window |
| `base_time_range` | Required when `comparator` is set; omit otherwise |
```json
"aggregation_attributes": {
"filter_operator": "and",
"filters": [
{
"attribute_name": "revenue",
"data_type": "double",
"aggregation_type": "sum",
"operator": "greaterThan",
"negate": false,
"value": 1000,
"is_dynamic_value": false
}
]
}
```
**Comparing against an earlier window.** When `comparator` is `change` or `percentageChange`, add `base_time_range` to define the window to compare against:
| Base window | `base_time_range` |
| ---------------- | ---------------------------------------------------------------------- |
| Previous period | `{ "type": "previousPeriod" }` |
| Fixed date range | `{ "type": "between", "value": "", "value1": "" }` |
```json
{
"attribute_name": "revenue",
"data_type": "double",
"aggregation_type": "sum",
"operator": "greaterThan",
"negate": false,
"value": 25,
"is_dynamic_value": false,
"comparator": "percentageChange",
"base_time_range": {
"type": "between",
"value": "2024-01-01T00:00:00.000Z",
"value1": "2024-01-31T23:59:59.999Z"
}
}
```
#### Portfolio (Multi-Project) Workspaces
In workspaces with more than one project, add `project_name` to scope a filter to a specific project. Both User Behavior and User Property filters accept the key. Omit it in single-project workspaces.
Set `project_name` to the name of the project you want to scope to. `"moe_portfolio"` is one of the available values and targets all projects.
***
### User Affinity Filter (`filter_type: "psychographic_event"`)
Targets users based on behavioral affinity over an event. `primary_time_range` and `psychographic_attributes` are required.
Psychographic attribute filters support only the `string` and `double` data types, use `category` values such as `"Event Attributes"`, and carry no `filter_type` key. The `primary_time_range` object also uses a different shape from User Behavior filters: relative windows use `days` (and `days1`) instead of `value`/`value1`, and absolute windows use `from` and `to` ISO 8601 dates, along with `type` and `value_type`.
#### Time-Based Affinity Filters
Four affinity filters target the time at which the event happens rather than an event attribute. Each uses the attribute name `moe_user_datetime`, `category` `"Time Attributes"`, `data_type` `"double"`, and an `extract_type`:
| `extract_type` | Meaning | Value range |
| ------------------- | ----------------- | ----------- |
| `time_of_the_day` | Hour of the day | 0–23 |
| `day_of_the_week` | Day of the week | 0–6 |
| `day_of_the_month` | Day of the month | 1–31 |
| `month_of_the_year` | Month of the year | 1–12 |
`value` follows the operator: a scalar for `is`, an array for `in`, and `value` plus `value1` for `between`.
```json
"psychographic_attributes": {
"filter_operator": "and",
"filters": [
{ "name": "moe_user_datetime", "data_type": "double", "category": "Time Attributes",
"operator": "is", "negate": false, "value": 9, "extract_type": "time_of_the_day" },
{ "name": "moe_user_datetime", "data_type": "double", "category": "Time Attributes",
"operator": "in", "negate": false, "value": [0, 6], "extract_type": "day_of_the_week" },
{ "name": "moe_user_datetime", "data_type": "double", "category": "Time Attributes",
"operator": "between", "negate": false, "value": 1, "value1": 31,
"extract_type": "day_of_the_month" }
]
}
```
| `operator_type` | Extra field | Meaning |
| --------------- | -------------------------- | ------------------------------------------------ |
| `predominant` | — | User most frequently exhibits this affinity |
| `minimum` | `percent_of_times` (1–100) | Affinity is present at least N% of the time |
| `top` | `percent_of_users` (1–100) | User is in the top N% by this affinity metric |
| `bottom` | `percent_of_users` (1–100) | User is in the bottom N% by this affinity metric |
***
### Custom Segment Filter (`filter_type: "custom_segments"`)
References a saved segment by ID. The backend resolves the segment by `id`; `name` is a display label only.
```json
{ "filter_type": "custom_segments", "id": "5c93982f573bb92004975a36", "name": "High-value customers" }
```
***
### Nested Filters (`filter_type: "nested_filters"`)
Use `nested_filters` inside `included_filters` or `excluded_filters` to create complex boolean logic (for example, `(A AND B) OR (C AND D)`). Groups can nest to arbitrary depth.
```json
{
"filter_type": "nested_filters",
"filter_operator": "or",
"filters": [
{ "filter_type": "user_attributes", ... },
{ "filter_type": "actions", ... }
]
}
```
# Get Segment by ID
Source: https://moengage.com/docs/api/filter-segments/get-segment-by-id
/api/custom-segments/custom-segments.yaml get /v3/custom-segments/{id}
This API fetches a specific segment (File or Filter) by its ID.
This API endpoint does not currently support Team-level scoping. All segments generated using this call will be assigned to the Default Team automatically.
#### Rate Limit
The rate limit is 100 requests/minute, 1000 requests/hour, and 4000 requests/day.
# List Segments
Source: https://moengage.com/docs/api/filter-segments/list-segments
/api/custom-segments/custom-segments.yaml get /v3/custom-segments
This API lists all segments. You can optionally filter segments by an exact name match.
This API endpoint does not currently support Team-level scoping. All segments generated using this call will be assigned to the Default Team automatically.
#### Rate Limit
The rate limit is 50 request/minute, 200 requests/hour, and 1000 requests/day.
# Update Filter Segment
Source: https://moengage.com/docs/api/filter-segments/update-filter-segment
/api/custom-segments/custom-segments.yaml patch /v3/custom-segments/{id}
This API updates an existing filter segment by its ID.
This API endpoint does not currently support Team-level scoping. All segments generated using this call will be assigned to the Default Team automatically.
# Flows Overview
Source: https://moengage.com/docs/api/flows/flows-overview
List, read, and control MoEngage Flows.
Use the Flows endpoints to discover flows, view a single flow (including its versions and stages), and change a flow's status. These endpoints read and control flows; they do not create them.
The Flows endpoints are in early access. MoEngage enhances these endpoints based on feedback during this period, but does not make breaking changes.
## Endpoints
The Flows API is a collection of the following endpoints:
* [Search Flows](/docs/api/flows/search-flows): Lists flows with filters and keyset pagination.
* [Get a Single Flow](/docs/api/flows/get-a-single-flow): Returns a single flow, and optionally a specific version with `version_no`.
* [Get a Specific Version of a Flow](/docs/api/flows/get-a-specific-version-of-a-flow): Returns a flow as it was in a specific version, addressed by its version ID.
* [Update Flow Status](/docs/api/flows/update-flow-status): Pauses, resumes, stops, retires, archives, or unarchives a flow.
## Authentication
Authentication is done via Basic Auth. This requires a Base64-encoded string of your credentials in the format `username:password`.
* **Username**: Use your MoEngage Workspace ID (also known as the App ID). Find it in the MoEngage dashboard at **Settings** > **Account** > **API keys**.
* **Password**: Use an API key from **Settings** > **Account** > **API keys**.
Refer to [API Key Dashboard](/docs/user-guide/settings/account/api-and-api-keys/api-key-dashboard) for details on creating and managing API keys.
When creating an API key for this API, ensure the **Campaigns** checkbox is selected under **Select APIs for access** (Flows reuse the Campaigns permissions). The key permissions you select determine your access level:
* **View** for read endpoints (Search Flows, Get a Single Flow, Get a Specific Version of a Flow)
* **Create & Manage** to update flow status
The workspace is resolved from the Basic Auth credentials in the `Authorization` header. You can also send an optional `X-MOE-Request-Id`.
Requests go to your data center's host, `https://api-{dc}.moengage.com`. For the full list of data centers and their identifiers, refer to [Data Centers](/docs/api/introduction#data-centers).
## Conventions
* IDs are raw 24-character ObjectIds. `flow_id` addresses the flow; `version_id` addresses one version.
* Every successful response uses the envelope `{response_id, type, data}`.
* Datetimes are ISO 8601 UTC (`YYYY-MM-DDThh:mm:ssZ`).
* Search results are paginated. Each response includes a `next_cursor`. To get the next page, send that value as the `cursor` parameter in your next request.
## Flow Structure and Nodes
**Get a single flow** and **Get a specific version of a flow** return the flow's stages in `data.structure.nodes[]`. Each entry is a node (one stage of the flow). Nodes share a common envelope and carry a `config` object whose shape depends on the node's `type` and `sub_type`.
Every node includes these fields:
| Field | Description |
| ----------------- | --------------------------------------------------------------------------------- |
| `stage_id` | Unique ID of the node within the flow. |
| `type` | Node category: `TRIGGER`, `ACTION`, `CONDITION`, `SPLIT`, `BRANCH`, or `CONTROL`. |
| `sub_type` | Specific variant within the `type`. |
| `parent_stage_id` | ID of the upstream node, or `null` for the entry (`TRIGGER`) node. |
| `child_stage_ids` | IDs of the immediate downstream nodes. |
| `label` | Human-readable label from the flow builder. May be `null`. |
| `config` | Type-specific configuration. Null-valued fields are omitted. |
The sections below describe the `config` shape for each node type. Field sets are documented from real flows; a node may include additional keys not shown here.
### Condition Nodes
A `CONDITION` node evaluates a condition and routes users down one of two paths. `child_stage_ids` always has exactly two entries: index `0` is the path taken when the condition matches, and index `1` is the path when it does not.
`config` fields:
* `condition` — the rule to evaluate. It reuses the same filter grammar as a trigger's event filter (`filter_operator`, `filters[]`, and per-filter `action_name`, `executed`, `execution.type`/`execution.count`, and nested `attributes`).
* `wait` — how long to wait before evaluating, as `{ duration, unit }`. `unit` is `mins`, `hours`, or `days`.
* `evaluation_timing` — when the condition is evaluated: `ON_ENTRY` or `SINCE_PREVIOUS_STAGE`.
Observed `sub_type` values: `HAS_DONE_EVENT`, `ON_EMAIL_CLICK`, `ON_EMAIL_OPEN`.
```json Two-way CONDITION node theme={null}
{
"stage_id": "Y8nSUSRe6",
"type": "CONDITION",
"sub_type": "HAS_DONE_EVENT",
"parent_stage_id": "ZL4rtyzrd",
"child_stage_ids": ["2iuY5k_zy", "TOGBwROjc7"],
"label": "Email Clicked",
"config": {
"condition": {
"filter_operator": "and",
"filters": [
{
"action_name": "MOE_EMAIL_CLICK",
"executed": true,
"filter_type": "actions",
"execution": { "count": 1, "type": "atleast" },
"attributes": { "filter_operator": "and", "filters": [] }
}
]
},
"wait": { "duration": 5, "unit": "mins" },
"evaluation_timing": "SINCE_PREVIOUS_STAGE"
}
}
```
### Branch Nodes
A `BRANCH` node represents a single arm of a parent `CONDITION` or `SPLIT`. Its `sub_type` mirrors the parent's `sub_type`, and its single child is the next real stage on that path. Its `config` depends on the parent:
* Arm of a `CONDITION` (explicit YES/NO pattern): `config` is `{ "outcome": "YES" }` or `{ "outcome": "NO" }`.
* Arm of a `CONDITIONAL_SPLIT`: `config` carries `branch_index` (0-based) and, for a conditioned arm, `branch_type: "main"` with a `condition` object. The catch-all arm uses `branch_type: "default"` and has no `condition`.
* Arm of an `AB_SPLIT` or `IPO`: `config` carries `name` and `percentage`.
```json BRANCH arms of a CONDITIONAL_SPLIT theme={null}
{
"stage_id": "KFs3PrkZJ",
"type": "BRANCH",
"sub_type": "CONDITIONAL_SPLIT",
"parent_stage_id": "f3OFPd5GNe",
"child_stage_ids": ["dJ0FjUCcC"],
"label": "Signed up and Purchased",
"config": {
"branch_type": "main",
"branch_index": 0,
"condition": { "filter_operator": "and", "filters": [] }
}
}
```
### Split Nodes
A `SPLIT` node splits users into more than two paths (or two paths for `IPO`). Its `child_stage_ids` map one-to-one to `BRANCH` children. `config` fields depend on the `sub_type`:
* `CONDITIONAL_SPLIT` routes each user down a branch based on a condition. Its `config` carries `branch_order` (the ordered list of child `stage_id`s), `evaluation_timing`, and `wait`.
* `AB_SPLIT` divides users randomly by percentage. Its `config.branches[]` lists each arm as `{ stage_id, name, percentage }`.
* `IPO` (Intelligent Path Optimizer) uses the same `config.branches[]` as `AB_SPLIT`, plus an `optimization_metric` (observed value: `ENGAGEMENT`). The ordering comes from `branches[]`, so there is no `branch_order`.
```json Multi-way CONDITIONAL_SPLIT theme={null}
{
"stage_id": "f3OFPd5GNe",
"type": "SPLIT",
"sub_type": "CONDITIONAL_SPLIT",
"child_stage_ids": ["KFs3PrkZJ", "CfwMAJthB", "EfjrVVREJ9"],
"label": "Has Signed Up and Purchased a Policy?",
"config": {
"evaluation_timing": "ON_ENTRY",
"branch_order": ["KFs3PrkZJ", "CfwMAJthB", "EfjrVVREJ9"],
"wait": { "duration": 0, "unit": "hours" }
}
}
```
```json AB_SPLIT theme={null}
{
"stage_id": "uMQdmCeJia",
"type": "SPLIT",
"sub_type": "AB_SPLIT",
"parent_stage_id": "CfwMAJthB",
"child_stage_ids": ["LsyB1exIx", "eElEw9Uaq8", "fxHKPCJ-M"],
"label": "A/B Split",
"config": {
"branches": [
{ "stage_id": "LsyB1exIx", "name": "Branch 1", "percentage": 33 },
{ "stage_id": "eElEw9Uaq8", "name": "Branch 2", "percentage": 33 },
{ "stage_id": "fxHKPCJ-M", "name": "Branch 3", "percentage": 34 }
]
}
}
```
### Action Nodes
An `ACTION` node sends a campaign or selects a channel. For a channel send (`EMAIL`, `PUSH`, `SMS`, `WHATSAPP`, and so on), `config` carries `campaign_id`, `channel`, and `campaign_name`.
The `NBA` (Next Best Action) sub\_type is an `ACTION` node — it occupies the same slot as channel sends, not a separate node type. Its `config` fields:
* `channels[]` — one entry per eligible channel, each `{ channel, stage_id, campaign_id }`. `stage_id` references a sibling `ACTION` node (of that channel) that owns the actual send; the NBA node itself holds no campaign content.
* `fallback_channel` — the channel used when the best channel can't be determined (observed value: `PUSH`).
* `best_time_to_send` — the send-time strategy (observed shape: `{ "default_time": "SEND_IMMEDIATELY" }`).
```json ACTION node with NBA sub_type theme={null}
{
"stage_id": "0-5G1JPQX",
"type": "ACTION",
"sub_type": "NBA",
"parent_stage_id": "abw_MALGPa",
"child_stage_ids": ["dgVoYPiUPT"],
"label": "Next Best Action",
"config": {
"channels": [
{ "channel": "PUSH", "stage_id": "x_-NueaX4X", "campaign_id": "69844f63316edd32518c5aef" },
{ "channel": "EMAIL", "stage_id": "zN9ibHPXbE", "campaign_id": "69844f63316edd32518c5af1" }
],
"fallback_channel": "PUSH",
"best_time_to_send": { "default_time": "SEND_IMMEDIATELY" }
}
}
```
### Control Nodes
A `CONTROL` node manages flow control and path convergence. Observed `sub_type` values:
* `EXIT` — exits the user from the flow.
* `WAIT_FOR_TIMER` — holds the user for a set duration.
* `GO_TO` — routes the user to a shared downstream stage (`config.target_stage_id`), used to re-converge parallel branches.
## FAQs
No, this isn't supported. You can search, read, and change the status of existing flows.
You do not enroll users directly. For event-triggered flows, use the Track Event API — a user enters the flow when a tracked event matches the flow's entry condition. For business-event-triggered flows, use the Trigger Business Event API. Periodic flows are audience-driven and do not support API enrollment.
`flow_id` identifies the flow. `version_id` identifies one of its versions. To read a specific version, either pass `version_no` on **Get a single flow**, or call **Get a specific version of a flow** with the `version_id`.
You can `pause`, `resume`, `stop`, `retire`, `archive`, and `unarchive` a flow. Each change is validated against the flow's current status; an invalid change returns a `409`. Publish isn't available.
The read endpoints (search, get flow, get version) require the `campaigns:view` permission. The status endpoint requires the `campaigns:create_manage` permission.
## Postman Collection
Test these endpoints quickly using our pre-configured Postman collection: [View MoEngage Flows APIs Collection](https://www.postman.com/moengage-dev/api-docs/collection/wtlm44a/moengage-flows-apis).
# Get a Single Flow
Source: https://moengage.com/docs/api/flows/get-a-single-flow
/api/flows/flows.yaml get /v5/flows/{flow_id}
This API returns the full details of one flow, including its settings, targeting, conversion
goal, and stage-by-stage structure. Requires the `campaigns:view` permission.
#### Rate Limits
The rate limits are at the workspace level. A maximum of 10 requests per second, 100 requests per minute, and 6,000 requests per hour are allowed per workspace.
# Get a Specific Version of a Flow
Source: https://moengage.com/docs/api/flows/get-a-specific-version-of-a-flow
/api/flows/flows.yaml get /v5/flows/{flow_id}/versions/{version_id}
This API returns a flow exactly as it was in a past version, addressed by its version ID.
Use it to audit what was live before a change. Requires the `campaigns:view` permission.
#### Rate Limits
The rate limits are at the workspace level. A maximum of 10 requests per second, 100 requests per minute, and 6,000 requests per hour are allowed per workspace.
# Search Flows
Source: https://moengage.com/docs/api/flows/search-flows
/api/flows/flows.yaml post /v5/flows/search
This API returns a paginated list of the flows in your workspace, filtered by the criteria
you send in the request body. Requires the `campaigns:view` permission.
#### Rate Limits
The rate limits are at the workspace level. A maximum of 10 requests per second, 100 requests per minute, and 6,000 requests per hour are allowed per workspace.
# Update Flow Status
Source: https://moengage.com/docs/api/flows/update-flow-status
/api/flows/flows.yaml patch /v5/flows/{flow_id}/status
This API changes a flow's lifecycle state. Requires the `campaigns:create_manage` permission.
Lifecycle actions (`pause`, `resume`, `stop`, `retire`) accept an optional `version_no` and act on that
version (default: the active version). `archive` / `unarchive` apply to the whole flow and reject a `version_no`.
| Action | Allowed from | Result |
| ----------- | ----------------------------------------------------------- | --------------------- |
| `pause` | `ACTIVE`, `SCHEDULED` | `PAUSED` |
| `resume` | `PAUSED` | `ACTIVE` |
| `stop` | `ACTIVE`, `SCHEDULED`, `PAUSED`, `RETIRED` | `STOPPED` |
| `retire` | `ACTIVE`, `PAUSED` | `RETIRED` |
| `archive` | `PAUSED`, `STOPPED`, `COMPLETED` (and not already archived) | sets `archived=true` |
| `unarchive` | any archived flow | sets `archived=false` |
#### Rate Limits
The rate limits are at the workspace level. A maximum of 10 requests per second, 100 requests per minute, and 6,000 requests per hour are allowed per workspace.
# GDPR or CCPA Overview
Source: https://moengage.com/docs/api/gdpr-ccpa/gdpr-ccpa-overview
Manage user data privacy rights and erasure requests to ensure GDPR and CCPA compliance.
The MoEngage GDPR or CCPA API ensures that all user data rights are respected and managed for compliance with the General Data Protection Regulation (GDPR) and the California Consumer Privacy Act (CCPA). This API allows you to submit data requests, including the permanent erasure of personal data for specific users.
For detailed implementation guides on how MoEngage handles these regulations, refer to our [GDPR Implementation](https://www.moengage.com/docs/user-guide/data/privacy/gdpr) and [CCPA Implementation](https://www.moengage.com/docs/user-guide/data/privacy/ccpa) documentation.
To update user attributes without erasing the user profile, use the [Track User](/docs/api/user/track-user) API instead.
## Endpoint
The GDPR or CCPA API consists of the following endpoint:
* [Submit a Data Request](/docs/api/gdpr/submit-a-gdpr-ccpa-data-request): Create or update user rights (e.g., erasure requests).
## FAQs
No. Multiple users cannot be deleted using this API in a single payload. You must send individual requests for different users unless they share the same email identity (see note above).
If multiple users have the same email ID and that email ID is passed in the erasure request, MoEngage will delete **all** users associated with that specific email ID.
The maximum payload size is 128 KB. If this limit is exceeded, a 413 error response will be sent.
## Postman Collections
Test your compliance workflows using our pre-configured Postman collection. [View Postman Collection →](https://www.postman.com/moengage-dev/api-docs/request/j4fwuwk/gdpr-ccpa-api?action=share\&source=copy-link\&creator=9636111)
# Submit a GDPR / CCPA Data Request
Source: https://moengage.com/docs/api/gdpr/submit-a-gdpr-ccpa-data-request
/api/gdpr-ccpa/gdpr-ccpa.yaml post /opengdpr_requests/{appId}
GDPR or CCPA API ensures all the rights of users are created or updated for GDPR or CCPA compliance. You can erase the personal data of specific users as defined under GDPR using the Erase API.
For more details on compliance with MoEngage, refer to [GDPR-Implementation](/user-guide/data/privacy/gdpr) and [CCPA-Implementation](/user-guide/data/privacy/ccpa).
**Warning:** Multiple users **cannot** be deleted using this API in a single payload. If an email ID associated with multiple users is passed for an erasure request, **all** associated users will be deleted.
#### Rate Limit
The maximum limit per request is **100 KB**. The maximum payload size is **128 KB**.
# Get Campaign Meta (V1 — Legacy)
Source: https://moengage.com/docs/api/get-campaign-details/get-campaign-meta-v1-—-legacy
/api/campaigns/campaigns.yaml post /campaigns/meta
This API retrieves campaign details and reachability information for scheduled campaigns.
**V1 endpoint**
This page documents the V1 Get Campaign Meta endpoint. The V5 equivalent is available at [Get Campaign Meta (V5)](/docs/api/get-campaign-details/get-campaign-meta). Both V1 and V5 use Basic Auth with a `MOE-APPKEY` header.
#### Supported Channels
* EMAIL: Email campaigns
* PUSH: Push notification campaigns
* SMS: SMS campaigns
* WHATSAPP: WhatsApp campaigns
* FACEBOOK: Facebook campaigns
* GOOGLE ADS: Google Ads campaigns
* CONNECTORS: Connector-based campaigns
#### Reachability Information
* Available only for **scheduled** campaigns (one-time, business event-triggered, and event-triggered).
* Provides estimated user count that will receive the campaign.
* Calculated once daily and cached for 24 hours.
* May vary due to app installations/uninstalls or subscription changes.
* Reachability is an estimated value and may vary over time. It is calculated once per day and cached for 24 hours. Multiple API calls within the same day will return the cached value.
#### Rate Limits
| Rate Limit Name | Rate Limit |
| :--------------------------- | :----------------------------------------------------------------------------------- |
| get campaign meta per second | The total number of get campaign meta requests per Second per client allowed is 10. |
| get campaign meta per minute | The total number of get campaign meta requests per minute per client allowed is 100. |
| get campaign meta per hour | The total number of get campaign meta requests per hour per client allowed is 6000. |
**Notes**
* Breaching the limits will reject the request.
* Per hour and per day limits will consider the calculation based on the last hour and last 24 hrs respectively.
# Get Campaign Meta (V5)
Source: https://moengage.com/docs/api/get-campaign-details/get-campaign-meta-v5
/api/campaigns/campaign-draft.yaml post /v5/campaigns/meta
Returns lightweight metadata for one or more campaigns without loading their full configuration.
#### Supported Channels
* Email
* Push
* SMS
* WhatsApp
* Facebook
* Google Ads
* Connectors
#### Reachability Information
* Available only for **scheduled** campaigns (one-time, business event-triggered, and event-triggered).
* Provides estimated user count that will receive the campaign.
* Calculated once daily and cached for 24 hours. Multiple API calls within the same day return the cached value.
* May vary due to app installations/uninstalls or subscription changes.
#### Rate Limits
| Rate Limit Name | Rate Limit |
| :--------------------------- | :----------------------------------------------------------------------------------- |
| get campaign meta per second | The total number of get campaign meta requests per second per client allowed is 10. |
| get campaign meta per minute | The total number of get campaign meta requests per minute per client allowed is 100. |
| get campaign meta per hour | The total number of get campaign meta requests per hour per client allowed is 6000. |
**Notes**
* Breaching the limits will reject the request.
* Per-hour and per-day limits use a rolling window of the last 1 hour and last 24 hours respectively.
# Get Campaign (V5)
Source: https://moengage.com/docs/api/get-campaign-details/get-campaign-v5
/api/campaigns/campaign-draft.yaml get /v5/campaigns/{campaign_id}
Returns the full configuration and current status of a single campaign by its ID.
**SMS campaigns:** SMS campaigns are returned by this endpoint; the response `channel` field will be `SMS`. The `connector` and `sender_name` fields carry SMS-specific details. SMS campaigns can be retrieved but must be created and managed through the MoEngage dashboard or V1 APIs in the interim.
#### Rate Limits
| Rate Limit Name | Rate Limit |
| :---------------------- | :------------------------------------------------------------------------------ |
| Get campaign per second | The total number of get campaign requests per second per client allowed is 10. |
| Get campaign per minute | The total number of get campaign requests per minute per client allowed is 100. |
| Get campaign per hour | The total number of get campaign requests per hour per client allowed is 6000. |
**Notes**
* Breaching the limits will reject the request.
* Per-hour limits use a rolling window of the last 1 hour.
# Get Child Campaigns
Source: https://moengage.com/docs/api/get-campaign-details/get-child-campaigns
/api/campaigns/campaigns.yaml post /campaigns/{parent_campaign_id}/executions
This API retrieves child campaign execution details for Periodic or Business Event-triggered campaigns. Use this API to track execution history of recurring campaigns and monitor the performance of individual instances.
**Not available in V5**
This endpoint is not yet available in V5. Use this V1 endpoint at `POST /core-services/v1/campaigns/{parent_campaign_id}/executions` until V5 support is added.
#### Information Retrieved
* Child campaign IDs
* Sent time for each execution
* Status of each child campaign
* Total number of times the parent campaign has been executed
**Note**
* Currently, you can use this API to get the child of Periodic and Business Event-triggered Email and Push campaigns.
* You can only retrieve child campaigns for campaigns created via the [Create Campaign API](https://www.moengage.com/docs/api/create-campaigns/create-campaign).
* Results are paginated with a maximum of 15 children per page.
#### Rate Limits
| Rate Limit Name | Rate Limit |
| :---------------------------- | :------------------------------------------------------------------------------------- |
| get child campaign per second | The total number of get child campaigns requests per second per client allowed is 10. |
| get child campaign per minute | The total number of get child campaigns requests per minute per client allowed is 100. |
| get child campaign per hour | The total number of get child campaigns requests per hour per client allowed is 6000. |
**Notes**
* Breaching the limits will reject the request.
* Per hour and per day limits will consider the calculation based on the last hour and last 24 hrs respectively.
# Search Campaigns
Source: https://moengage.com/docs/api/get-campaign-details/search-campaigns
/api/campaigns/campaigns.yaml post /campaigns/search
This API fetches a list of Push, Email, or SMS campaigns with all current fields and status. You can pass multiple filters to find specific campaigns.
#### Search Capabilities
* Filter by channel, delivery type, status, tags, name, ID, created by, created date
* Include child campaigns (flow nodes, periodic children)
* Include archived campaigns
* Paginated results (max 15 per page)
**Differences in V5**
The V5 Search Campaigns API (`POST /v5/campaigns/search`) introduces the following behavioral changes:
* **Single-campaign retrieval** is no longer handled by the search endpoint. Use `GET /v5/campaigns/{campaign_id}` instead.
* The `campaign_fields.id` field (string) is renamed to `campaign_fields.ids` (array of strings) in V5.
* The `campaign_fields.delivery_type` field is renamed to `campaign_fields.campaign_delivery_type` in V5. Sending the V1 field name returns zero results with no error.
* `request_id` is required in V1. In V5, it is optional.
* The response identifier field `campaign_id` is renamed to `id` in V5.
For a full comparison, see [V1 vs. V5 Search Campaigns: Behavioral Differences](/docs/api/campaigns/search-campaigns-v5-migration).
#### Rate Limits
| Rate Limit Name | Rate Limit |
| :------------------------- | :--------------------------------------------------------------------------------- |
| Search campaign per second | The total number of search campaign requests per second per client allowed is 10. |
| Search campaign per minute | The total number of search campaign requests per minute per client allowed is 100. |
| Search campaign per hour | The total number of search campaign requests per hour per client allowed is 6000. |
**Notes**
* Breaching the limits will reject the request.
* Per hour and per day limits will consider the calculation based on the last hour and last 24 hrs respectively.
# Search Campaigns (V5)
Source: https://moengage.com/docs/api/get-campaign-details/search-campaigns-v5
/api/campaigns/campaign-draft.yaml post /v5/campaigns/search
Returns the full V5 campaign payload for all campaigns that match the specified filters.
Supports pagination and filtering by channel, status, tags, name, IDs, dates, delivery
type, and creator.
#### Rate Limits
| Rate Limit Name | Rate Limit |
| :------------------------- | :--------------------------------------------------------------------------------- |
| Search campaign per second | The total number of search campaign requests per second per client allowed is 10. |
| Search campaign per minute | The total number of search campaign requests per minute per client allowed is 100. |
| Search campaign per hour | The total number of search campaign requests per hour per client allowed is 6000. |
**Notes**
* Breaching the limits will reject the request.
* Per-hour and per-day limits use a rolling window of the last 1 hour and last 24 hours respectively.
# Create In-app Template
Source: https://moengage.com/docs/api/in-app-templates/create-in-app-template
/api/in-app-templates/in-app-templates.yaml post /custom-templates/inapp
This API creates an In-app template in MoEngage. You can use this API to upload templates created outside the MoEngage ecosystem to MoEngage and use them for campaign creation.
**Information**
Only self-handled and HTML template types are supported.
# In-app Templates Overview
Source: https://moengage.com/docs/api/in-app-templates/in-app-templates-overview
Create, search, and update In-app templates (Self-Handled and HTML).
The MoEngage In-app Templates API allows you to seamlessly manage In-app templates within the MoEngage platform. This API enables you to upload templates created outside the MoEngage ecosystem—supporting both **Self-Handled** and **HTML** types—so they can be used for campaign creation.
With these endpoints, you can create versions of your templates, search through your existing library using various filters, and choose whether updates to a template should automatically reflect in your active campaigns.
## Endpoints
The In-app Templates API is a collection of the following endpoints:
* [Create In-app Template](/docs/api/in-app-templates/create-in-app-template): Creates a new In-app template (Self-Handled or HTML).
* [Update In-app Template](/docs/api/in-app-templates/update-in-app-template): Updates an existing template.
* [Search In-app Templates](/docs/api/in-app-templates/search-in-app-templates): Finds templates using filters like name, source, type, or creator ID.
## FAQs
Currently, the API supports **SELF\_HANDLED** (JSON-based) and **INAPP\_HTML** template types.
When using the **Update In-app Template** API, set the `update_campaigns` flag to `true`. This will update all running campaigns currently using that template to the latest version.
## Postman Collection
Test these endpoints quickly by importing our Postman collection: [**View in Postman**](https://www.postman.com/moengage-dev/api-docs/collection/wada9u5/moengage-in-app-api?action=share\&source=copy-link\&creator=28342601)
# Search In-app Templates
Source: https://moengage.com/docs/api/in-app-templates/search-in-app-templates
/api/in-app-templates/in-app-templates.yaml post /custom-templates/inapp/search
This API searches the In-app templates created in your MoEngage account.
**Mandatory Pagination**
We are introducing mandatory pagination, effective **November 15, 2025**. All calls to this API must include the following two parameters:
* `page`: The page number of the results you wish to fetch.
* `entries`: The number of templates to return per page, with a maximum value of *15*.
Please update all integrations to include these parameters. API requests submitted without them after the effective date will result in an error and fail to execute.
# Update In-app Template
Source: https://moengage.com/docs/api/in-app-templates/update-in-app-template
/api/in-app-templates/in-app-templates.yaml put /custom-templates/inapp
This API updates an In-app template as per your requirements.
# Inform Overview
Source: https://moengage.com/docs/api/inform/inform-overview
Send a transactional alert to a user on one or more channels.
The MoEngage Inform API allows you to send a transactional alert to a user on one or more channels using a pre-configured Alert ID or Alert Reference Name.
## Supported Scenarios
Transactional Alerts are designed for critical user communications, including:
* Order/Booking/Purchase confirmations
* Shipping/Delivery confirmations and updates
* Security and account alerts
* Password resets
* OTP (one-time password)
* User invitations and shares
* User inaction related to previous transactions
* Fraud prevention/security alerts
This API sends notifications to all the channels simultaneously for a specified Live Alert. You can use it to do the following:
* Send a transactional message on a single channel like SMS or Email or Push.
* Send transactional messages on multiple channels.
## Endpoint
The Inform API consists of the following endpoint:
* [Inform API](/docs/api/transactional-alerts/send-transactional-alert)
## Glossary
The following is a list of terminology that you will encounter when using the Inform API.
| Term | Definition |
| ----------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Alert ID** | This is the unique identifier for an Alert. This field will be used to identify the Alert configured in the MoEngage Dashboard that contains the template for the notifications to be sent to the user. |
| **Alert Reference Name** | This field is used to identify the Alert using your reference Name and can be used to identify the alert as an alternative to Alert ID. |
| **Test Alert ID** | When creating an Alert, you can test it on an external console and MoEngage provides a unique identifier called Test Alert ID for this purpose. |
| **Test Alert Reference Name** | Test Alert Reference Name is configured while adding the alert on the Dashboard to send this Test Alert request to an external console. |
## Live vs. Test Environments
Ensure you are using the correct ID and Endpoint for your environment.
| Feature | Live Alerts | Test Alerts |
| :------------- | :------------------------------------------------ | :-------------------------------------------------------- |
| **Identifier** | Use the **Alert ID** for published alerts. | Use the **Test Alert ID** for testing. |
| **Logs** | Logs are available in the **Alert Info** section. | Logs are available in the **3rd Step** of Alert Creation. |
| **Endpoint** | `https://api-0{dc}.moengage.com` | `https://sandbox-api-0{dc}.moengage.com` |
## How Does Inform API Work and Respond?
Inform API works in two steps as described below:
1. A check is performed to verify if the API request received for Inform is valid.
2. If valid, the second process is to resolve the content and send a message using the partner.
### Response Codes
The response codes are shared in **wxyzab** format as described below:
* **w** - Status (failures are indicated with 1 and success is indicated with 2).
* **x** - Category of the error.
* **y** - Request is considered for parallel request (parallel requests is indicated with 0 and fallback requests are indicated with 1).
* **z** - Can this request be retried by you(0 indicates it cannot be retried with same details).
* **ab** - Reason details for the error.
Below are the Category details:
| Category Code | Details |
| :------------ | :-------------------------------- |
| 1 | Unauthorized |
| 2 | Bad Request |
| 4 | Internal Server Error / Ratelimit |
The Reason details are given below with each Reason Code holding specific meanings:
| Reason Code | Details |
| :---------- | :-------------------------- |
| 1 | Invalid Credentials |
| 2 | Invalid Alert ID |
| 3 | Missing Transaction ID |
| 4 | Duplicate Transaction ID |
| 5 | Long Transaction ID |
| 6 | Invalid Payload |
| 7 | Personalisation Attribute |
| 9 | Internal Server Error |
| 10 | Rate Limit |
| 11 | Personalisation Failed |
| 12 | Invalid Recipient details |
| 13 | Vendor configuration errors |
| 14 | Vendor not available |
| 15 | Vendor Payload Rejection |
| 16 | Unexpected Error Occurred |
| 17 | Invalid Origin Source |
## Payload Errors Before Accepting the API Request
The list of the payload errors that can occur before accepting the API request and the equivalent response codes to be shared under HTTP status code **4xx** for different scenarios across each functionality is given below:
| Payload Errors | Description | Scenarios | Response Codes | Resolution |
| :--------------------------------------- | :----------------------------------------------------------------------------------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :---------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------- |
| **Authorization Failure** | This error occurs when invalid authorization details such as App Key (Workspace ID) or Password are shared. | No Auth values passed. Incorrect Auth Values Passed Incorrect Password shared. No Auth & No Key Passed No App key shared / Empty App Key Incorrect App Key shared | `{"message": "Authorization Failed", "err_code": "UNAUTHORIZED"}` | Recheck the authorization details listed in Settings / Check if you passed the Test Alert ID on Live Endpoint. |
| **Invalid Alert ID** | This error occurs when an invalid Alert ID or external reference name is shared. | Missing Alert ID Alert ID \<24 characters Alert ID > 24 Characters Invalid Alert ID | `{"message": "Invalid Alert ID", "err_code": "BAD_REQUEST", "status_code": 120002}` | Recheck the Alert ID or external reference name listed on the Info page. |
| **Invalid Transaction ID** | This error occurs when the Transaction ID is not shared. | Missing Transaction ID | `{"message": "Invalid Transaction ID", "err_code": "BAD_REQUEST", "status_code": 120003}` | Share the transaction ID using the key name `transaction_id` as part of the API Call. |
| **Long Transaction ID** | This error occurs when the Transaction ID is invalid. | Long Transaction ID | `{"message": "Long Transaction ID", "err_code": "BAD_REQUEST", "status_code": 120004}` | Reduce the length of the Transaction ID to 50 characters. |
| **Duplicate Transaction ID** | This error occurs when the currently shared Transaction ID matches the previous one in the last 5 minutes. | Duplicate Transaction ID | `{"message": "Duplicate Transaction ID", "err_code": "DUPLICATE_REQUEST_RECEIVED", "status_code": 120105}` | Add a unique Transaction ID. |
| **Invalid Payload** | This error occurs when the payload is in incorrect JSON format or when incorrect Channel details are shared. | Invalid JSON / Invalid Class Channel Attribute is incorrect / Channel details missing | `{"message": "Invalid Payload", "err_code": "BAD_REQUEST", "status_code": 120006}` | Recheck the JSON format and the Channel details. Sample payload is available on our Info page. |
| **Personalisation Attribute Max length** | This error occurs when the Personalization Attribute length is greater than 1500 characters. | - | `{"message": "Personalisation attribute limit exceeded - {x}", "err_code": "BAD_REQUEST", "status_code": 120007}` | Reduce the length of the Personalization Attribute to 1500 characters / payload size of \<100 KB |
| **Invalid Recipient** | This error occurs when the recipient details like mobile number or email address are invalid. | - | `{"message": "Invalid Recipient", "err_code": "BAD_REQUEST", "status_code": 120012}` | Recheck the recipient details in the API call or the User Profile. |
| **Internal Server Error** | This error occurs when MoEngage is not able to resolve the API request. | - | `{"message": "Unexpected error occurred", "err_code": "Internal Server Error", "status_code": 140109}` | Retry using the same API call with a different Transaction ID. |
| **Rate Limit** | This error occurs when you cross the Rate Limit. | - | `{"message": "Too many requests", "err_code": "Rate Limit", "status_code": 140110}` | Make API calls within the Rate Limit. |
## Payload Errors After Accepting the API Request
Alerts can be configured for parallel/sequential fallback as described below:
* **Parallel**: For parallel requests, the payload information of success/failure requests should be added at each channel level.
* **Sequence**: For fallback requests, the payload information of success/failure requests should be added for the first channel, and other channels, info should be shared over streams.
The list of the payload details that can occur after accepting the API request and the equivalent response codes to be shared under HTTP status code \*\*200/429 \*\*for different scenarios across each functionality is given below:
| Payload Errors | Description | Response Codes | Resolution |
| :---------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | :--------------------------------------------------------------------------------------------------------------------- |
| **Personalisation Failed** | This error occurs when the required Personalization attributes are not shared in the API call or User Profile doesn't have these values. | `{"message": "Successfully Received", "Request_ID": 122323, "SMS": {"message": "Personalisation Failure - {{attribute name}}", "err_code": "BAD_REQUEST", "status_code": "120011 / 121011"}}` | Either share the valid Personalization attributes in the API call or update the User Profile with the required values. |
| **Invalid Recipient details** | This error occurs when the recipient details like mobile number or email address are invalid. | `{"message": "Successfully Received", "Request_ID": 122323, "SMS": {"message": "Invalid Recipient", "err_code": "BAD_REQUEST", "status_code": "120012 / 121012"}}` | Recheck the recipient details in the API call or the User Profile. |
| **Internal Errors** | This error occurs when MoEngage is not able to resolve the API request or channel level details. | `{"message": "Successfully Received", "Request_ID": 122323, "SMS": {"message": "Unexpected error occurred", "err_code": "Internal Server Error", "status_code": "140109 / 141109"}}` | Retry using the same API call with a different Transaction ID. |
| **Vendor Errors** | This error occurs when the vendor configuration is invalid. | `{"message": "Successfully Received", "Request_ID": 122323, "SMS": {"message": "Invalid Vendor Configuration", "err_code": "Vendor Error", "status_code": "150013 / 151013"}}` | Recheck the vendor configuration in MoEngage for SMS, Push, and Email. |
| **Vendor Unavailable** | This error occurs when the vendor is not available. | `{"message": "Successfully Received", "Request_ID": 122323, "SMS": {"message": "Vendor not available", "err_code": "Vendor Error", "status_code": "150014 / 151014"}}` | NA/MoEngage will retry up to 5 times to submit the message to the vendor. |
| **Vendor Rejected** | This error occurs when the vendor rejects the Payload. | `{"message": "Successfully Received", "Request_ID": 122323, "SMS": {"message": "Rejected by Vendor with error - {{error from vendor}}", "err_code": "Vendor Error", "status_code": "150015 / 151015"}}` | Resolve the error sent by the vendor and then make the API call. |
| **Vendor Accepted** | This is sent as response when vendor accepts the request. | `{"message": "Successfully Received", "Request_ID": 122323, "SMS": {"message": "Successfully sent", "err_code": "NA", "status_code": 200000}}` | - |
## FAQs
A request is deemed a duplicate in the following cases:
* It is received within 5 minutes of a previously successful request containing the same transaction\_id
* It is received within 5 minutes of a previous request which is ‘In Progress’ (being processed) and contains the same transaction\_id
Duplicate requests are dropped and not reprocessed.
When the vendor does not accept requests from MoEngage, MoEngage retries every request a maximum of five times with 200ms exponential backoff.
Alert logs are stored for up to 30 days for every Alert in MoEngage.
This error occurs when you hit the live alert ID at the sandbox endpoint. For every alert, there are 2 IDs: one is live, and another is Test; the same behavior can be seen for alerts created on the test environment of the MoEngage Dashboard as well. Test Alert ID can be picked up by editing the Alert and going to the 3rd page.
## Postman Collection
Test these endpoints quickly by importing our Postman collection: [View in Postman](https://www.postman.com/moengage-dev/api-docs/collection/n3e0uj9/moengage-inform-api).
# API Documentation
Source: https://moengage.com/docs/api/introduction
Explore MoEngage REST APIs for managing user data, campaigns, segments, templates, and integrations.
The MoEngage REST APIs let you manage user data, campaigns, segments, content, and integrations without the dashboard. Every endpoint uses standard HTTP methods, Basic Authentication, and JSON request and response bodies.
This reference covers the available API categories, along with the data centers, base URLs, authentication, rate limits, and error handling that apply across them.
## API Categories
Create and update users, track events, manage devices, and import data in bulk.
Create and trigger business events to power automated campaigns.
Manage templates, content blocks, recommendations, coupons, and catalogs.
Create and manage Push and Email campaigns programmatically.
Create and manage file-based, filter-based, and cohort-synced user segments.
Manage email subscriptions and subscription category preferences.
Access custom dashboards and fetch the analytics data behind their charts.
Send transactional and targeted push notifications to Android, iOS, and Web.
Fetch and manage App Inbox cards for users.
Send transactional alerts across SMS, Email, and Push channels.
Start, update, and end iOS Live Activities via broadcast.
Fetch and manage personalized experiences for users.
View and retrieve archived messages.
***
## Data Centers
MoEngage maintains multiple data centers. You are assigned to a specific data center when you sign up. You can identify your data center from your dashboard URL.
| Data Center | Dashboard URL | REST API Host |
| ----------- | ------------------------------------ | ------------------------------ |
| DC-01 | `https://dashboard-01.moengage.com` | `https://api-01.moengage.com` |
| DC-02 | `https://dashboard-02.moengage.com` | `https://api-02.moengage.com` |
| DC-03 | `https://dashboard-03.moengage.com` | `https://api-03.moengage.com` |
| DC-04 | `https://dashboard-04.moengage.com` | `https://api-04.moengage.com` |
| DC-05 | `https://dashboard-05.moengage.com` | `https://api-05.moengage.com` |
| DC-06 | `https://dashboard-06.moengage.com` | `https://api-06.moengage.com` |
| DC-101 | `https://dashboard-101.moengage.com` | `https://api-101.moengage.com` |
### Choosing a Data Center
If you have data privacy requirements to store user data in a specific geographical region:
* **US region**: Sign up with DC-01 or DC-04
* **EU region**: Sign up with DC-02
* **India region**: Sign up with DC-03
* **Indonesia region**: Sign up with DC-06
DC-05 (Singapore) is not available for new sign-ups. Existing DC-05 workspaces remain operational and continue to use `https://api-05.moengage.com` for API requests. For data residency requirements in Singapore, contact your Customer Success Manager.
After data is captured in a workspace, it cannot be migrated to a different data center. Always use the REST API endpoint matching your registered data center.
***
## Base URL
All API requests are made to:
```text theme={null}
https://api-{dc}.moengage.com
```
Replace `{dc}` with your data center number (e.g., `01`, `02`, `03`). You can identify your data center from your Dashboard URL.
Each API may have additional path segments (e.g., `/v1`, `/core-services/v1`). Refer to the specific API documentation for the complete endpoint path.
***
## Authentication
All MoEngage API requests require Basic Authentication. To authenticate, you must include a Base64-encoded string of your credentials in the Authorization header of every request. Basic Authentication sends a Base64-encoded string containing your username and password with every API request. It encodes a 'username:password' string in Base64 and appends the encoded string with 'Basic '. This string is included in the authorization header as shown below:
`{"Authorization: Basic Base64_ENCODED_WORKSPACEID_APIKEY=="}`
### Required Headers
```http theme={null}
Authorization: Basic {base64_encoded_credentials}
MOE-APPKEY: {your_workspace_id} # This header is mandatory for the File Import and Test Connection APIs. It is not required for API Endpoints under Data.
Content-Type: application/json
```
The `MOE-APPKEY` header (set to your Workspace ID) is required only for File Import APIs and the Test Connection API. It is not required for core Data APIs such as Track User, Get User, Track Event, Merge User, Delete User, and Track Device.
### Generating Credentials
The `Authorization` header value is a Base64 encoding of `workspace_id:api_key`.
```bash theme={null}
# Example: Encoding credentials
echo -n "YOUR_WORKSPACE_ID:YOUR_API_KEY" | base64
```
### Getting Your Credentials
1. Log in to the [MoEngage dashboard](https://dashboard.moengage.com).
2. Navigate to **Settings** > **Account** > **APIs**.
3. Copy your **Workspace ID** and the relevant **API Key**.
You can perform authentication using a client like Postman as follows:
### API Keys by Feature
Different APIs require different API keys from your dashboard:
| API | API Key Location (Settings → Account → APIs) |
| --------------------- | -------------------------------------------------------------------------- |
| Data | Data |
| Push | Push |
| Inform | Inform |
| Campaigns and Catalog | Campaign report/Business events/Custom templates/Catalog API/Inform Report |
| Personalize | Personalize |
## Rate Limits
MoEngage enforces per-workspace rate limits and payload caps on its REST APIs. Requests that exceed a rate limit receive an `HTTP 429` response; oversized payloads receive `HTTP 413` (or `400`). Rate-limited responses include `x-ratelimit-limit`, `x-ratelimit-remaining`, and `x-ratelimit-reset` headers so you can track remaining capacity in real time.
For the full per-endpoint breakdown — rate limits, payload size caps, monitoring headers, and how to request an increase — see [Rate Limits](/docs/api/rate-limits).
***
## Error Handling
Most MoEngage APIs return errors in the standard format below.
### Standard Error Format
```json wrap theme={null}
{
"status": "fail",
"error": {
"message": "The request parameters are invalid",
"type": "Bad Request",
"request_id": "abc123xyz"
}
}
```
Newer v5 APIs, such as [Flows](/docs/api/flows/flows-overview), use a different error envelope: `{ response_id, error: { code, message, target, details } }`. Refer to the specific API's reference for its exact error shape.
### HTTP Status Codes
| Code | Description |
| ----- | ---------------------------------- |
| `200` | Success |
| `201` | Created |
| `202` | Accepted (async processing) |
| `400` | Bad Request - Invalid parameters |
| `401` | Unauthorized - Invalid credentials |
| `403` | Forbidden - Access denied |
| `404` | Not Found - Resource doesn't exist |
| `409` | Conflict - Duplicate resource |
| `413` | Payload Too Large |
| `429` | Rate Limit Exceeded |
| `500` | Internal Server Error |
***
## Key Endpoints Reference
### Data
| Method | Endpoint | Description |
| ------ | ------------------------------------ | ------------------------ |
| `POST` | `/customer/{Workspace_ID}` | Create or update user |
| `POST` | `/customers/export` | Get user details |
| `POST` | `/customer/merge` | Merge two users |
| `POST` | `/customer/delete` | Delete users in bulk |
| `POST` | `/event/{Workspace_ID}` | Track user events |
| `POST` | `/transition/{Workspace_ID}` | Bulk import |
| `POST` | `/device/{app_id}` | Create or update device |
| `POST` | `/devices/manage` | Manage devices |
| `POST` | `/fileimports/trigger/{schedule_id}` | Trigger file import |
| `POST` | `/fileimports/import/status` | Get import status |
| `GET` | `/installInfo` | Get install info |
| `POST` | `/integrations/authentication` | Validate authentication |
| `POST` | `/opengdpr_requests/{appId}` | Create GDPR/CCPA request |
### Business Events
| Method | Endpoint | Description |
| ------ | ------------------------- | ---------------------- |
| `POST` | `/business_event` | Create business event |
| `POST` | `/business_event/trigger` | Trigger business event |
| `POST` | `/business_event/search` | Search business events |
### Content
#### Templates
| Method | Endpoint | Description |
| ------ | -------------------------------- | ------------------------ |
| `POST` | `/custom-templates/inapp` | Create In-App template |
| `PUT` | `/custom-templates/inapp` | Update In-App template |
| `POST` | `/custom-templates/inapp/search` | Search In-App templates |
| `POST` | `/custom-templates/osm` | Create OSM template |
| `PUT` | `/custom-templates/osm` | Update OSM template |
| `POST` | `/custom-templates/osm/search` | Search OSM templates |
| `POST` | `/custom-templates/sms` | Create SMS template |
| `PUT` | `/custom-templates/sms` | Update SMS template |
| `POST` | `/custom-templates/sms/search` | Search SMS templates |
| `POST` | `/email-templates` | Create Email template |
| `GET` | `/email-templates` | Get all Email templates |
| `GET` | `/email-templates/{id}` | Get Email template by ID |
| `PUT` | `/email-templates/{id}` | Update Email template |
| `POST` | `/custom-templates/email` | Create Email template V2 |
| `PUT` | `/custom-templates/email` | Update Email template V2 |
| `POST` | `/custom-templates/push` | Create Push template |
| `PUT` | `/custom-templates/push` | Update Push template |
| `POST` | `/custom-templates/push/search` | Search Push templates |
#### Content Blocks
| Method | Endpoint | Description |
| ------ | ---------------------------- | ------------------------- |
| `POST` | `/content-blocks` | Create content block |
| `PUT` | `/content-blocks` | Update content block |
| `POST` | `/content-blocks/get-by-ids` | Get content blocks by IDs |
| `POST` | `/content-blocks/search` | Search content blocks |
#### Recommendations
| Method | Endpoint | Description |
| ------ | -------------------------------------------- | ----------------------------------- |
| `GET` | `/recommendations` | List all recommendations |
| `GET` | `/recommendations/{recommendation_id}` | Get the details of a recommendation |
| `POST` | `/recommendations/{recommendation_id}/items` | Get recommended items for a user |
#### Coupons
| Method | Endpoint | Description |
| -------- | ------------------------------------------------------ | -------------------- |
| `POST` | `/coupon-list` | Create coupon list |
| `GET` | `/coupon-list` | List coupon lists |
| `GET` | `/coupon-list/{coupon_list_id}` | Get coupon list |
| `PATCH` | `/coupon-list/{coupon_list_id}` | Update coupon list |
| `PUT` | `/coupon-list/{coupon_list_id}/activate` | Activate coupon list |
| `PUT` | `/coupon-list/{coupon_list_id}/archive` | Archive coupon list |
| `POST` | `/coupon-list/{coupon_list_id}/files` | Upload coupon file |
| `GET` | `/coupon-list/{coupon_list_id}/files` | List coupon files |
| `GET` | `/coupon-list/{coupon_list_id}/files/{coupon_file_id}` | Get coupon file |
| `DELETE` | `/coupon-list/{coupon_list_id}/files/{coupon_file_id}` | Delete coupon file |
| `POST` | `/coupon-list/{coupon_list_id}/usage-report` | Get usage report |
#### Catalog
| Method | Endpoint | Description |
| ------- | ----------------------------------------- | ---------------------- |
| `POST` | `/catalog` | Create catalog |
| `PATCH` | `/catalog/{catalog_id}/attributes` | Add catalog attributes |
| `POST` | `/catalog/{catalog_id}/items` | Ingest catalog items |
| `POST` | `/catalog/{catalog_id}/items/search` | Search catalog items |
| `PATCH` | `/catalog/{catalog_id}/items` | Update catalog items |
| `POST` | `/catalog/{catalog_id}/items/bulk-delete` | Delete catalog items |
### Campaigns
| Method | Endpoint | Description |
| ------- | ------------------------------------------------ | ------------------------ |
| `POST` | `/campaigns` | Create campaign |
| `PATCH` | `/campaigns/{campaign_id}` | Update campaign |
| `POST` | `/campaigns/search` | Search campaigns |
| `POST` | `/campaigns/test` | Test campaign |
| `POST` | `/personalization/preview` | Preview personalization |
| `POST` | `/campaigns/meta` | Get campaign metadata |
| `POST` | `/campaigns/status` | Get campaign status |
| `POST` | `/campaigns/{parent_campaign_id}/executions` | Get campaign executions |
| `POST` | `/core-services/v1/campaign-stats` | Get campaign stats |
| `GET` | `/campaign_reports/rest_api/{APP_ID}/{FILENAME}` | Download campaign report |
### Segments
| Method | Endpoint | Description |
| ------- | ----------------------------------------------- | ------------------------- |
| `POST` | `/v2/custom-segments/file-segment` | Create file segment |
| `PUT` | `/v2/custom-segments/file-segment/add-users` | Add users to segment |
| `PUT` | `/v2/custom-segments/file-segment/remove-users` | Remove users from segment |
| `PUT` | `/v2/custom-segments/file-segment/replace` | Replace segment users |
| `GET` | `/v3/custom-segments` | List filter segments |
| `POST` | `/v3/custom-segments` | Create filter segment |
| `GET` | `/v3/custom-segments/{id}` | Get segment by ID |
| `PATCH` | `/v3/custom-segments/{id}` | Update filter segment |
| `POST` | `/v1/integrations/cohortsync` | Sync cohort audience |
| `PATCH` | `/v2/custom-segments/archive` | Archive segment |
| `PATCH` | `/v2/custom-segments/unarchive` | Unarchive segment |
### Subscriptions
| Method | Endpoint | Description |
| ------ | ------------------------------------------ | ------------------------------- |
| `POST` | `/emails/v1.0/bulk-resubscribe` | Bulk resubscribe emails |
| `PUT` | `/v1.0/opt-in-management/user-preferences` | Update opt-in preferences |
| `GET` | `/category-subscription/user-preferences` | Get subscription preferences |
| `PUT` | `/category-subscription/user-preferences` | Update subscription preferences |
| `POST` | `/category-subscription/user-preferences` | Create subscription preferences |
### Push
| Method | Endpoint | Description |
| ------ | ----------------------- | ---------------------- |
| `POST` | `/transaction/sendpush` | Send push notification |
### Cards
| Method | Endpoint | Description |
| -------- | --------------- | ------------ |
| `POST` | `/cards/fetch` | Fetch cards |
| `DELETE` | `/cards/delete` | Delete cards |
### Inform
| Method | Endpoint | Description |
| ------ | -------------- | ----------------------------------------- |
| `POST` | `/alerts/send` | Send transactional alert (SMS/Email/Push) |
### Live Activities
| Method | Endpoint | Description |
| ------ | --------------------------------- | -------------------- |
| `POST` | `/live-activity/broadcast/start` | Start Live Activity |
| `POST` | `/live-activity/broadcast/update` | Update Live Activity |
| `POST` | `/live-activity/broadcast/end` | End Live Activity |
### Personalize
| Method | Endpoint | Description |
| ------ | ----------------------- | ----------------------- |
| `POST` | `/experiences/fetch` | Fetch experiences |
| `GET` | `/experiences/metadata` | Get experience metadata |
| `POST` | `/experiences/events` | Track experience events |
### Message Archival
| Method | Endpoint | Description |
| ------ | ---------------- | ---------------------- |
| `POST` | `/archival/view` | View archived messages |
***
## Support
Need help with the API?
* **Support**: Contact your Customer Success Manager or [raise a support ticket](https://www.moengage.com/docs/user-guide/contact-support/raise-a-support-ticket-through-moengage-dashboard).
# Add Items
Source: https://moengage.com/docs/api/items/add-items
/api/catalog/catalog.yaml post /catalog/{catalog_id}/items
This API ingests items into an existing catalog as long as the attributes provided during ingestion match the attributes provided during catalog creation.
#### Rate Limit
* Request limit: You can ingest 100 items per minute OR 1000 items per hour. You can ingest up to 50 items per request.
* Payload size limit: 5 MB only when Content-Length header is provided.
# Delete Items
Source: https://moengage.com/docs/api/items/delete-items
/api/catalog/catalog.yaml post /catalog/{catalog_id}/items/bulk-delete
This API deletes existing items in a given catalog.
#### Rate Limit
* Request limit: You can delete 100 items per minute OR 1000 items per hour. You can delete up to 50 items per request.
* Payload size limit: 5 MB only when Content-Length header is provided.
# Get Items
Source: https://moengage.com/docs/api/items/get-items
/api/catalog/catalog.yaml post /catalog/{catalog_id}/items/search
This API retrieves item attribute details for catalog items using their unique item IDs. The attributes can include the title, price, category, link, image_link, and the respective creation date.
#### Rate Limit
* Request limit: You can get 100 item attribute details per minute OR 1000 item attribute details per hour. You can request up to 50 items per request.
* Payload size limit: 5 MB only when the Content-Length header is provided.
The limit is a COMBINED limit across all Catalog APIs for a specific user.
# Update Items
Source: https://moengage.com/docs/api/items/update-items
/api/catalog/catalog.yaml patch /catalog/{catalog_id}/items
This API updates items with new attribute values. Attributes must adhere to the data type defined.
#### Rate Limit
* Request limit: You can update 100 items per minute OR 1000 items per hour. You can update up to 50 items per request.
* Payload size limit: 5 MB only when Content-Length header is provided.
# End Broadcast Live Activity
Source: https://moengage.com/docs/api/live-activities/end-broadcast-live-activity
/api/live-activities/live-activities.yaml post /live-activity/broadcast/end
This API terminates a broadcast Live Activity across all subscribed devices simultaneously, with options for a final state update or immediate dismissal.
#### Rate Limit
The rate limit for this endpoint is 500 requests per minute per workspace and 5 requests per minute per live activity ID.
# Live Activities Overview
Source: https://moengage.com/docs/api/live-activities/live-activities-overview
Initiate, update, and terminate shared real-time activities for iOS users.
The MoEngage Broadcast Live Activities API enables brands to manage shared, real-time experiences for large audiences on iOS. This is ideal for live sporting events, election tracking, or breaking news where multiple users need to see the same real-time updates on their lock screens or Dynamic Island.
Live Activities can only be managed for push campaigns created with the **BROADCAST\_LIVE\_ACTIVITY** delivery type via the [Create Push Campaigns API](/docs/api/create-campaigns/create-campaign). These campaigns cannot be created through the MoEngage UI.
## Endpoints
The Broadcast Live Activities API consists of the following endpoints:
* [**Start Broadcast Live Activity**](/docs/api/live-activities/start-broadcast-live-activity): Initiates the shared activity and sends the initial content to the target audience.
* [**Update Broadcast Live Activity**](/docs/api/live-activities/update-broadcast-live-activity): Pushes unified updates (e.g., score changes) to all subscribed devices.
* [**End Broadcast Live Activity**](/docs/api/live-activities/end-broadcast-live-activity): Terminates the activity session across all devices.
## FAQs
No. Live Activities are a native iOS feature. This API specifically targets iOS devices supporting the ActivityKit framework.
The `broadcast_live_activity_id` refers to the overarching campaign ID created via the Push API. The `instance_id` is an optional field you can provide to track a specific session or prevent duplicate requests for the same event.
Content is updated via the `content_state` JSON object. The keys within this object must match the `ContentState` struct defined in your iOS application code.
## Postman Collection
Test these endpoints quickly by importing our Postman collection: [View in Postman](https://www.postman.com/moengage-dev/api-docs/folder/etd05kq/start-live-activity)
# Start Broadcast Live Activity
Source: https://moengage.com/docs/api/live-activities/start-broadcast-live-activity
/api/live-activities/live-activities.yaml post /live-activity/broadcast/start
This API initiates a shared, real-time activity for a large audience, such as a live sporting match or real-time election result tracking.
You can start live activities for only push campaigns created with the BROADCAST\_LIVE\_ACTIVITY delivery type using the [Create Push Campaigns API](https://www.moengage.com/docs/api/create-campaigns/create-campaign), not campaigns created through the MoEngage UI.
#### Rate Limit
The rate limit for this endpoint is 5 requests per minute per workspace.
# Update Broadcast Live Activity
Source: https://moengage.com/docs/api/live-activities/update-broadcast-live-activity
/api/live-activities/live-activities.yaml post /live-activity/broadcast/update
This API pushes a single, unified update to all subscribed Live Activities, such as an updated game score or a new development in a breaking news story.
#### Rate Limit
The rate limit for this endpoint is 500 requests per minute per workspace and 5 requests per minute per live activity ID.
# List Locales
Source: https://moengage.com/docs/api/locales/list-locales
/api/locales/locales.yaml get /v5/locales
Returns all locales in the workspace, or a single locale when you supply `name` or `id`. Use either `name` or `id`, not both. Omit both parameters to list every locale in the workspace (paginated).
#### Rate Limits
The rate limits are at the workspace level. A maximum of 60 requests per minute and 1,000 requests per hour are allowed per workspace.
# Locales Overview
Source: https://moengage.com/docs/api/locales/locales-overview
List the locales configured in your workspace and look up a single locale by name or ID.
A locale is a named audience definition — built from user-property filters — that you use to localize campaign messages for a language, culture, or region. For example, a locale named `es-MX` might target users whose last known country is Mexico.
The MoEngage Locales API allows you to list the locales configured in your workspace and look up a single locale by name or ID. To create or edit a locale, use the MoEngage dashboard. For more information, see [Locales](/docs/user-guide/settings/advanced-settings/locales).
## Endpoints
The Locales API is a collection of the following endpoints:
* [List Locales](/docs/api/locales/list-locales): Returns all locales in the workspace, or a single one specified by name or ID.
## Authentication
Authentication is done via Basic Auth. This requires a Base64-encoded string of your credentials in the format `username:password`.
* **Username**: Use your MoEngage Workspace ID (also known as the App ID). Find it in the MoEngage dashboard at **Settings** > **Account** > **API keys**.
* **Password**: Use an API key from **Settings** > **Account** > **API keys**.
Refer to [API Key Dashboard](/docs/user-guide/settings/account/api-and-api-keys/api-key-dashboard) for details on creating and managing API keys.
## Pagination
The list response returns at most 20 locales per page. When `pagination.has_more` is `true`, pass `pagination.next_cursor` back as the `cursor` query parameter to fetch the next page. Treat the cursor as opaque — do not decode or modify it.
## FAQs
### Manage Locales
No. This endpoint is read-only. Create and edit locales from the MoEngage dashboard. For more information, see [Locales](/docs/user-guide/settings/advanced-settings/locales).
Pass either `name` or `id` to [List Locales](/docs/api/locales/list-locales). Supplying both returns a `400` error. Omit both to list every locale in the workspace.
No. There is no limit to the number of locales you can create in your workspace.
No. Locale names are unique within a workspace, which is why you can look up a locale by its exact name.
### Understand the Response
It is the number of campaigns currently using the locale. This matches the **Campaigns associated** column on the Locales page in the dashboard.
A locale is defined by user-property filters, which MoEngage stores as a custom segment. `custom_segment_id` identifies that segment. It is returned only for locales that have one.
## Related Content
* [Locales](/docs/user-guide/settings/advanced-settings/locales): Add and manage locales from the MoEngage dashboard.
* [Localize Campaign Messages](/docs/user-guide/campaigns-and-channels/getting-started/campaign-content/localize-campaign-messages): Use locales to deliver campaign content in a user's language.
## Postman Collection
Test this endpoint using our pre-configured Postman collection: [View MoEngage Locales Collection](https://www.postman.com/moengage-dev/api-docs/collection/q8t2fuz/moengage-locale-list-api-v5?action=share\&source=copy-link\&creator=3486165).
# Archive Segment
Source: https://moengage.com/docs/api/manage-segments/archive-segment
/api/custom-segments/custom-segments.yaml patch /v2/custom-segments/archive
This API archives an existing segment (File or Filter). Archiving and unarchiving through APIs makes it easy to retrieve and reuse segments whenever required for purposes such as A/B testing, maintaining regulatory compliance, and improving system performance. You can unarchive an archived segment to reuse it in campaigns and analysis without recreating it from scratch.
Archived segments will not be shown beyond 180 days.
This API endpoint does not currently support Team-level scoping. All segments generated using this call will be assigned to the Default Team automatically.
# Unarchive Segment
Source: https://moengage.com/docs/api/manage-segments/unarchive-segment
/api/custom-segments/custom-segments.yaml patch /v2/custom-segments/unarchive
This API unarchives an existing segment, making it active again.
This API endpoint does not currently support Team-level scoping. All segments generated using this call will be assigned to the Default Team automatically.
# Message Archival Overview
Source: https://moengage.com/docs/api/message-archival/messaage-archival-overview
Retrieve and view archived copies of sent communication across Push, Email, and SMS.
The MoEngage Message Archival API provides a way to store and access historical records of communications sent to your customers. By utilizing this API, you can retrieve the exact content—including titles, messages, and media URLs—that was delivered to a specific user at a specific time.
This is particularly useful for customer support teams who need to verify what a customer received, or for compliance audits where a historical record of messaging is required.
## Endpoint
The Message Archival API consists of the following endpoint:
* [View Archived Message](/docs/api/message-archival/view-archived-message): Retrieves the full content and metadata of a specific message sent to a user via Push, Email, or SMS.
## FAQs
To retrieve a message, you must provide the `user_id`, the `campaign_id`, and the `sent_epoch_time` (Unix timestamp) of the communication. For event-triggered campaigns, passing the `event_id` is also required.
* **message\_content**: Contains the actual personalized content sent to the user.
* **backup\_content**: Contains the original template or default content used if personalization failed or was not applied.
# Message archival overview
Source: https://moengage.com/docs/api/message-archival/message-archival-overview
# View Archived Message
Source: https://moengage.com/docs/api/message-archival/view-archived-message
/api/message-archival/message-archival.yaml post /archival/view
Use this API to fetch (view) archived message content that was previously sent to customers.
#### Rate Limit
The rate limit for this endpoint is **1000 RPM**.
# Offerings Overview
Source: https://moengage.com/docs/api/offerings/offerings-overview
Create, update, and list offerings for Offer Decisioning in MoEngage.
The MoEngage Offerings API allows you to manage offerings. You can create time-bound promotions and personalized content, update scheduling or targeting on existing offerings, list offerings for reporting or orchestration workflows, and list the personalization templates available for offering content.
If this API is not enabled for your account, contact your MoEngage Customer Success Manager (CSM) or the Support team to request enablement.
## Offering lifecycle
An offering moves through different states automatically based on its scheduling window. There is no separate publish step — the offering goes live as soon as it is created, if the scheduling window is current.
| State | Meaning |
| :---------- | :------------------------------------------------------------------------------------------------------------------------------------------------ |
| `scheduled` | Indicates that the `start_datetime` is in the future. The offering is not yet eligible for decisioning. |
| `active` | Indicates that the current time is between `start_datetime` and `expiry_datetime`. The offering is eligible for decisioning. |
| `expired` | Indicates that the `expiry_datetime` has passed. The offering is no longer served and cannot be updated via the API. |
| `archived` | Indicates that the offering is manually archived from the dashboard. The offering is excluded from decisioning and cannot be updated via the API. |
| `draft` | Indicates that the offering is not yet published. Offerings created via the API are never in `draft` state. |
## Endpoints
The Offerings API is a collection of the following endpoints:
* [List Offerings](/docs/api/public-offerings/list-offerings): Returns a paginated list of offerings, with filtering by ID, name, status, tags, date range, and creator.
* [Create Offering](/docs/api/public-offerings/create-offering): Creates an offering in your workspace, including content, scheduling, segmentation, and variation configuration.
* [Update Offering](/docs/api/public-offerings/update-offering): Partially updates an existing offering. Only the fields in the request body are changed.
* [List Offer Templates](/docs/api/public-offerings/list-offer-templates): Returns a paginated list of the personalization templates available in your workspace. Use a template `id` as `meta.templateId` in offering content.
There is no endpoint to fetch a single offering by ID.
## Authentication
Authentication is done via Basic Auth. This requires a Base64-encoded string of your credentials in the format `username:password`.
* **Username**: Use your MoEngage Workspace ID (also known as the App ID). Find it in the MoEngage dashboard at **Settings** > **Account** > **API keys**.
* **Password**: Use an API key from **Settings** > **Account** > **API keys**.
Refer to [API Key Dashboard](/docs/user-guide/settings/account/api-and-api-keys/api-key-dashboard) for details on creating and managing API keys.
When creating an API key for this API, ensure the **Offerings** checkbox is selected under **Select APIs for access**. The key permissions you select determine your access level:
* **View** for read endpoints (list offerings and templates)
* **Create & Manage** to create and update offerings
* **Create, Manage & Publish** to publish offerings
* **Download** to download offering reports
## Rate Limits
Rate limits apply per consumer. Each endpoint has its own limit:
| Endpoint | Method | Rate limit |
| :---------------------- | :----- | :----------------------------------- |
| `/v5/offers` | GET | 150 requests/min, 500/hour, 1000/day |
| `/v5/offers/templates` | GET | 150 requests/min, 500/hour, 1000/day |
| `/v5/offers` | POST | 100 requests/min, 300/hour, 600/day |
| `/v5/offers/{offer_id}` | PATCH | 100 requests/min, 300/hour, 600/day |
Exceeding a limit returns a `429` response with a `Retry-After` header indicating how many seconds to wait before retrying.
## Idempotency
The Create and Update endpoints require an `Idempotency-Key` header (UUID v4). Reusing the same key within 24 hours returns the original response without re-running the operation, so retries on network failures are safe.
Two conflict cases return `409`:
* **`DUPLICATE_IDEMPOTENCY_KEY`** - a request with the same key is already in progress. Wait for it to complete before retrying.
* **`IDEMPOTENCY_CONFLICT`** - the key was already used within 24 hours with a different request body. Use a new UUID v4 key to submit the changed payload.
Use the **same key** only when retrying an identical request after a network failure or timeout. Use a **new key** for every logically distinct operation, including any request where the payload has changed.
## Errors
The Offerings API uses standard HTTP status codes and returns all errors in a consistent JSON envelope.
### HTTP status codes
| Code | Meaning |
| :---- | :-------------------------------------------------------------------------------------- |
| `200` | Success (GET, PATCH). |
| `201` | Offering created (POST). |
| `400` | Validation failed. One or more fields are invalid, missing, or violate a business rule. |
| `401` | Authentication failed. Credentials are missing or invalid. |
| `403` | Forbidden. The feature is not enabled, or the offering is expired or archived. |
| `404` | Offering not found. The `offer_id` in the path does not exist in this workspace. |
| `409` | Idempotency conflict. See [Idempotency](#idempotency). |
| `429` | Rate limit exceeded. Retry after the number of seconds in the `Retry-After` header. |
| `503` | Gateway unavailable. Treat as transient and retry with exponential backoff. |
### Error response shape
All `4xx` and `5xx` responses return a JSON body in the following format:
```json theme={null}
{
"error": {
"code": "VALIDATION_FAILED",
"message": "One or more fields failed validation.",
"target": "scheduling.expiry_datetime",
"details": [
{
"code": "REQUIRED_FIELD_MISSING",
"target": "variation_meta",
"message": "variation_meta is required."
},
{
"code": "INVALID_SCHEDULING",
"target": "scheduling",
"message": "expiry_datetime must be after start_datetime."
}
],
"doc_url": "https://www.moengage.com/docs/api/offerings/offerings-overview"
},
"response_id": "resp_c5f83262-3127-4e23-bc1b-9efd4c929e12"
}
```
| Field | Description |
| :---------------- | :---------------------------------------------------------------------------------------------------------------------------------------------------- |
| `error.code` | Machine-readable error code in `ALL_CAPS_SNAKE_CASE`. Use this to branch on specific errors in your code. |
| `error.message` | Human-readable explanation of the error. |
| `error.target` | The field or resource that caused the error, in dot-notation (e.g. `scheduling.expiry_datetime`). |
| `error.details[]` | Per-field violations for `400 VALIDATION_FAILED` responses. The API collects all errors before responding — a single `400` may list multiple entries. |
| `error.doc_url` | Link to documentation for this error code. |
| `response_id` | Trace identifier for this response. Always present, even on errors. Provide this to MoEngage support when reporting an issue. |
The `details` array is only present on `400 VALIDATION_FAILED` responses. All other error codes return only the top-level `error` object.
### Common error codes
For the full list of error codes per endpoint, see the individual endpoint pages. The most commonly encountered codes across all Offerings API operations are:
| Code | HTTP | Meaning |
| :-------------------------- | :--- | :------------------------------------------------------------------------------------- |
| `VALIDATION_FAILED` | 400 | One or more request fields failed validation. Check `error.details` for the full list. |
| `REQUIRED_FIELD_MISSING` | 400 | A required field is absent. `error.target` identifies which field. |
| `INVALID_SCHEDULING` | 400 | `expiry_datetime` is not after `start_datetime`, or a datetime is in the past. |
| `INVALID_OFFER_NAME` | 400 | `name` contains characters other than letters, numbers, and underscores. |
| `DUPLICATE_OFFER_NAME` | 400 | `name` matches an existing non-archived offering in the workspace. |
| `INVALID_TAG_ID` | 400 | One or more tag IDs in `tags` do not exist in the workspace. |
| `IMMUTABLE_FIELD` | 400 | An attempt was made to change a status-locked field. |
| `MISSING_IDEMPOTENCY_KEY` | 400 | The `Idempotency-Key` header is absent. |
| `INVALID_IDEMPOTENCY_KEY` | 400 | The `Idempotency-Key` is not a valid UUID v4. |
| `DUPLICATE_IDEMPOTENCY_KEY` | 409 | A request with the same key is in-flight. |
| `IDEMPOTENCY_CONFLICT` | 409 | The key was reused with a different request body. |
| `EXPIRED_OFFERING` | 403 | The offering is past its `expiry_datetime` and cannot be modified. |
| `ARCHIVED_OFFERING` | 403 | The offering is archived and cannot be modified. |
| `OFFER_NOT_FOUND` | 404 | No offering with the given ID exists in this workspace. |
## FAQs
### Getting Started
Yes. Make sure the Offer Decisioning feature is enabled for your workspace — if it isn't, all API calls return `403 FORBIDDEN`. Contact your MoEngage CSM or the Support team to request enablement.
Before calling the Create Offering API, ensure the following workspace resources already exist:
* **Tags** — create and manage them in the MoEngage dashboard under **Settings → Advanced Settings → Tags**. The tag IDs you pass in the `tags` array must already exist.
* **Offering attributes** — configured under **Offer Decisioning → Attributes** in the dashboard. Pass their IDs in `offering_attribute_configuration`.
* **Personalization templates** — list available templates using `GET /v5/offers/templates` and use the returned `id` as `meta.templateId` in your content.
In the MoEngage dashboard, go to **Settings** > **Account** > **API keys**, copy the Workspace ID, and create a key with access to the Personalize APIs. Use the Workspace ID as the Basic Auth username and the API key as the password. For the full steps, refer to [Authentication](#authentication).
### Offering Lifecycle
An offering is always in one of five states: `scheduled`, `active`, `expired`, `archived`, or `draft`.
See the [Offering lifecycle](#offering-lifecycle) section above for the full state table.
Currently, there is no dedicated `GET /v5/offers/{offer_id}` endpoint. To retrieve a specific offering, call [List Offerings](/docs/api/public-offerings/list-offerings) with the `id` query parameter set to the offering's ID (including the `offer_` prefix). This returns a single-item list for that offering.
### Create Offering
The required top-level fields are: `name`, `priority`, `delivery`, `created_by`, `scheduling` (with both `start_datetime` and `expiry_datetime`), `segment_info`, `offer_content`, and `variation_meta`. All other fields — `tags`, `capping_rules`, `conversion`, `imp_track_hours`, `offering_attribute_configuration` — are optional and can be omitted.
It depends on the offering's status and variation `type`:
* While the offering is `scheduled`, the entire `variation_meta` object is editable.
* Once the offering is `active`, the `type` and the `variantsPerLocale` key set are locked. For `SMV`, you can still update the `smv_distribution` split percentages and `control_group.percentage`. For `DMV`, only `control_group.percentage` is editable.
Editing a locked field returns `IMMUTABLE_FIELD`. The variation types are `SMV` (static allocation you control) and `DMV` (dynamic allocation MoEngage optimizes).
You include all fields in a single Create request — there is no incremental or draft-building flow. The offering's initial status is set from the scheduling dates: `scheduled` when `start_datetime` is in the future, `active` when the current time is within the scheduling window, or `expired` when `start_datetime` is already in the past.
Priority is an integer from 1 to 100 used by Decision Policies to rank eligible offerings for a user. Higher values rank the offering higher relative to others with lower priority. The exact ranking behavior also depends on any scoring formulas configured in the associated Decision Policy and any `offering_attribute_configuration` scores.
Offering names must be 5–100 characters and contain only letters, numbers, and underscores. Spaces, hyphens, and special characters are not allowed. Names must be unique within the workspace. Example of a valid name: `Summer_Sale_Promo_2026`.
### Update Offering
You can update `name`, `description`, `priority`, `tags`, `scheduling` (within state-based rules below), `segment_info`, `offer_content`, `capping_rules`, `is_global_control_enabled`, `imp_track_hours`, `offering_attribute_configuration`, and `conversion`. You can also update `variation_meta`, but only within the status-based and type-based rules described below.
| Field | `scheduled` | `active` |
| :----------------------------------------- | :---------- | :--------- |
| `scheduling.start_datetime` | ✅ Editable | ❌ Locked |
| `scheduling.expiry_datetime` | ✅ Editable | ✅ Editable |
| `conversion` | ✅ Editable | ❌ Locked |
| `offer_content` | ✅ Editable | ✅ Editable |
| `name`, `tags`, `priority`, `segment_info` | ✅ Editable | ✅ Editable |
| `variation_meta.type`, `variantsPerLocale` | ✅ Editable | ❌ Locked |
| `variation_meta.smv_distribution` (`SMV`) | ✅ Editable | ✅ Editable |
| `variation_meta.smv_distribution` (`DMV`) | ✅ Editable | ❌ Locked |
| `variation_meta.control_group.percentage` | ✅ Editable | ✅ Editable |
No. The Update endpoint is a PATCH — only the fields you include in the request body are changed. Omitted fields retain their current values. However, `tags` and `offering_attribute_configuration` are **replaced entirely** when included, not merged. To add a single tag, send the current tag list plus the new one. To remove all tags, send an empty array.
No. The API returns `403` with `EXPIRED_OFFERING` or `ARCHIVED_OFFERING`. Only offerings in `active` and `scheduled` states can be updated.
The `offer_id` (including the `offer_` prefix) is returned in the `data.id` field of the Create Offering response. You can also retrieve it by calling [List Offerings](/docs/api/public-offerings/list-offerings) and filtering by name or status.
### Idempotency
Use the **same key** only when retrying a request that failed due to a network error or timeout — where you are not sure whether the server processed the original request. Reusing the same key within 24 hours returns the original response without re-running the operation.
Use a **new key** for every logically distinct operation, and for any retry where the request body has changed from the previous attempt.
The API returns `409 IDEMPOTENCY_CONFLICT`. The original operation is not replayed and the new payload is not processed. Generate a new UUID v4 key and resubmit.
UUID v4 (for example, `550e8400-e29b-41d4-a716-446655440000`). For automated workflows, generate a fresh UUID v4 per distinct operation. Do not derive the key from the request payload — if the payload is identical to a previous request, an intentional replay would return the cached response.
# Update User Email Opt-in Preferences
Source: https://moengage.com/docs/api/opt-in-management/update-user-email-opt-in-preferences
/api/email-subscription/email-subscription.yaml put /v1.0/opt-in-management/user-preferences
This API updates a user's overall email opt-in status and/or category-level subscription preferences within MoEngage. This API is typically used after a user submits the second confirmation through MoEngage consent-seeking emails (Double Opt-in).
#### Rate Limit
The rate limit is **1000 RPM** (requests per minute) and **360K** requests per day.
If you use PII Tokenization for your emails, ensure you pass the user ID when calling the Email Opt-in Management API. MoEngage uses this user ID to locate the user and update their opt-in status. Passing the email ID is ineffective in this case, as MoEngage does not store it.
If you use PII Encryption to send emails, ensure you pass either the user ID or the decrypted email ID. MoEngage uses this information to locate the associated user and update their opt-in status, or to update the opt-in status of all users having that email ID.
# Create OSM Template
Source: https://moengage.com/docs/api/osm-templates/create-osm-template
/api/osm-templates/osm-templates.yaml post /custom-templates/osm
This API creates a new On-Site Messaging (OSM) template. You can use this API to upload templates created outside the MoEngage ecosystem to MoEngage and use them for campaign creation.
API templates cannot be used in the Drag and Drop Editor in the MoEngage Dashboard. They are only supported for the Custom HTML Editor.
#### Rate Limit
The rate limit is 100 RPM. You can upload a maximum of 100 templates per minute.
# OSM Templates Overview
Source: https://moengage.com/docs/api/osm-templates/osm-templates-overview
Create, search, and update On-Site Messaging (OSM) templates.
The MoEngage On-Site Messaging (OSM) Templates API allows you to manage your web and app-site messaging templates. You can use this API to upload custom HTML templates created outside the MoEngage ecosystem, making them available for your OSM campaigns.
This API supports multiple template formats including **Banners**, **Pop-ups**, and **Nudges**. Note that templates created via this API are supported within the **Custom HTML Editor** in the MoEngage Dashboard and cannot be used with the Drag and Drop Editor.
## Endpoints
The OSM Templates API is a collection of the following endpoints:
* [Create OSM Template](/docs/api/osm-templates/create-osm-template): Creates a new OSM template with an HTML payload.
* [Update OSM Template](/docs/api/osm-templates/update-osm-template): Updates an existing OSM template and handles campaign versioning.
* [Search OSM Templates](/docs/api/osm-templates/search-osm-templates): Retrieves templates based on filters like template type, source, or creator.
## FAQs
The API currently supports the creation and management of **BANNER**, **POP\_UP**, and **NUDGE** template types. All payloads must be in HTML format.
No. Templates uploaded via the API are only compatible with the **Custom HTML Editor** in the MoEngage Dashboard.
When updating a template, you can set the `update_campaigns` flag.
* **True**: Automatically updates all running campaigns to the new template version.
* **False**: Creates a new version without affecting existing campaigns.
## Postman Collection
Test these endpoints quickly by importing our Postman collection: [**View in Postman**](https://www.postman.com/moengage-dev/api-docs/collection/28342601-e2d062c4-11b3-45cc-9249-a58d939c3497?action=share\&source=copy-link\&creator=28342601)
# Search OSM Templates
Source: https://moengage.com/docs/api/osm-templates/search-osm-templates
/api/osm-templates/osm-templates.yaml post /custom-templates/osm/search
This API searches the OSM templates created in your MoEngage account.
**Mandatory Pagination**
We are introducing mandatory pagination, effective **November 15, 2025**. All calls to this API must include the following two parameters:
* `page`: The page number of the results you wish to fetch.
* `entries`: The number of templates to return per page, with a maximum value of *15*.
Please update all integrations to include these parameters. API requests submitted without them after the effective date will result in an error and fail to execute.
* If the request body is passed empty, then all templates will be returned.
* The `preview_image` field in the response will be generated by MoEngage for the HTML template payload.
#### Rate Limit
The rate limit is 100 RPM.
# Update OSM Template
Source: https://moengage.com/docs/api/osm-templates/update-osm-template
/api/osm-templates/osm-templates.yaml put /custom-templates/osm
This API updates an OSM template as per your requirements.
#### Rate Limit
The rate limit is 100 RPM.
# Personalize Overview
Source: https://moengage.com/docs/api/personalize-experience/personalize-overview
Fetch personalized content and report experience events from your server-side codebase.
Personalize APIs constitute a set of endpoints that allow you to deliver tailored user experiences directly from your platform's code. Unlike client-side personalization, these server-side APIs must be invoked within your rendering pipeline to receive relevant data.
Once your platform receives the personalized payload, you can use it to dynamically render content—such as banners, product recommendations, or pricing—specifically for the requesting user. MoEngage automatically evaluates targeting rules, in-session attributes, and segments to return the correct variation.
## Endpoints
The Personalize API suite is categorized into experience management and event reporting:
* [Fetch Experience](/docs/api/experiences/fetch-experience): Evaluates targeting rules and returns the personalized payload (variations) for a user.
* [Fetch Experience Metadata](/docs/api/experiences/fetch-experience-metadata): Retrieves a list of all currently Active, Scheduled, or Paused experiences.
* [Track Experience Events](/docs/api/events/track-experience-events): Tracks impressions (shown) and user interactions (clicked) for accurate campaign reporting.
## Reporting Experience Events
You can report impressions and clicks for server-side experiences using either the API or a MoEngage SDK, depending on your platform:
| Method | Platforms | Notes |
| ----------------------------------------------------------------------------------------------------------------------------------- | --------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| [Track Experience Events API](/docs/api/events/track-experience-events) | All (Web, Mobile, TV, etc.) | **Recommended.** Works across all platforms and keeps tracking logic consistent with server-side content delivery. The API request to report clicks remains the same as the one to report impressions. The only change is to the value of the **action** field (*MOE\_PERSONALIZATION\_MESSAGE\_SHOWN* or *MOE\_PERSONALIZATION\_MESSAGE\_CLICKED*). |
| [MoEngage Web SDK](/docs/developer-guide/personalize-sdk/sdk-integration/personalize-api-experience-events-tracking) | Web | Use the Personalize SDK to report experience events from a website. |
| [MoEngage Android SDK](/docs/developer-guide/android-sdk/data-tracking/advanced-or-optional/personalize-experience-events-tracking) | Android | Report experience events natively from an Android app. |
| [MoEngage iOS SDK](/docs/developer-guide/ios-sdk/data-tracking/advanced/personalize-experience-events-tracking) | iOS | Report experience events natively from an iOS app. |
## FAQs
### Experience Management
Yes. By passing multiple values in the `experience_key` array (e.g., `["hero-banner", "sidebar-promo"]`), you can receive personalized content for multiple sections of your page in one request.
The API primarily uses the `customer_id`. If that is inaccurate or missing, you can use `user_identifiers` (like email or mobile number) as a fallback, provided **Identity Resolution** is enabled in your MoEngage workspace.
### Security and Events
Only regenerate your API Secret in the event of a security breach. Note that once a new secret is saved, all existing integrations using the old key will immediately stop working.
For server-side personalization, using the **Report Experience Events API** is recommended for all platforms (Web, Mobile, TV) as it keeps the tracking logic consistent with the content delivery logic.
You can use the `b_id` field within the events payload to uniquely identify which element was clicked (e.g., "Add to Cart" vs "Wishlist") within the same personalized experience.
# Create Offering
Source: https://moengage.com/docs/api/public-offerings/create-offering
/api/offerings/offerings.yaml post /v5/offers
Create a new Offering in the workspace.
#### Rate Limit
The rate limit is 100 requests per minute, 300 requests per hour, and 600 requests per day per consumer.
# List Offer Templates
Source: https://moengage.com/docs/api/public-offerings/list-offer-templates
/api/offerings/offerings.yaml get /v5/offers/templates
Fetch the personalization templates available in the workspace.
#### Rate Limit
The rate limit is 150 requests per minute, 500 requests per hour, and 1000 requests per day per consumer.
# List Offerings
Source: https://moengage.com/docs/api/public-offerings/list-offerings
/api/offerings/offerings.yaml get /v5/offers
Fetch all offerings in the workspace. Returns a summary of each offering — ID, name,
status, tags, and creation metadata.
#### Rate Limit
The rate limit is 150 requests per minute, 500 requests per hour, and 1000 requests per day per consumer.
# Update Offering
Source: https://moengage.com/docs/api/public-offerings/update-offering
/api/offerings/offerings.yaml patch /v5/offers/{offer_id}
Modify an existing offering. Only the fields you include in the request body are
changed.
#### Rate Limit
The rate limit is 100 requests per minute, 300 requests per hour, and 600 requests per day per consumer.
# Push Templates Overview
Source: https://moengage.com/docs/api/push-templates/push-templates-overview
Create, update, and search for push notification templates.
The MoEngage Push Templates API allows you to create, update, and search for push notification templates for Android and iOS platforms. Use these endpoints to manage your template library efficiently and integrate it into your automated workflows.
If this API is not enabled for your account, contact your MoEngage Customer Success Manager (CSM) or the Support team to request enablement.
## Endpoints
The Push Templates API is a collection of the following endpoints:
* [Create Push Template](/docs/api/templates/create-push-template): Creates a push template.
* [Update Push Template](/docs/api/templates/update-push-template): Updates a push template specified by its Template ID.
* [Search for Push Templates](/docs/api/templates/search-for-push-templates): Fetches a push template using its Template ID or other filters like template name, platform, and so on.
## FAQs
Yes, you can send only the iOS payload if you want to create a template for iOS.
Audio and video: All types of video and audio file formats supported by standard FCM/APNS platforms are supported.
**Images:** The following formats are supported: "image/png", "image/jpeg", "image/jpg", "image/gif".
Yes, you can create multiple templates with the same name, provided they have different versions.
## Postman Collections
We have made it easy for you to test the APIs. Click here to view the [Postman collection](https://www.postman.com/moengage-dev/api-docs/folder/3182294-9043f92c-dd0c-4ad7-b619-df7d4b7c4d87).
# Push API
Source: https://moengage.com/docs/api/push/push-api
api/push/push-v2-1.yaml POST /v2.1/transaction/sendpush
This API creates and sends a push notification campaign. You can use this API to create campaigns (targeting all users or a group of users) to send notifications, target a single user using a unique user attribute, and personalize payload for each user.
This endpoint accepts three request headers: `X-MOE-APPKEY` (your Workspace ID), `X-MOE-PushAPI-Signature` (the authorization signature), and `X-MOE-Query-Type` (the target audience, corresponding to the `targetAudience` field in the request body).
#### Rate Limit
The rate limit is 10,000 requests per minute.
# Push API (Legacy)
Source: https://moengage.com/docs/api/push/push-api-legacy
api/push/push.yaml POST /transaction/sendpush
This API creates and sends a push notification campaign. You can use this API to create campaigns (targeting all users or a group of users) to send notifications, target a single user using a unique user attribute, and personalize payload for each user.
#### Rate Limit
The rate limit is 10,000 requests per minute.
#### Migrating to v2.1
This version does not use a separate query-type header — MoEngage resolves the request category directly from the `targetAudience` field in the request body. The [v2.1 endpoint](/api/push/push-api) introduces the `X-MOE-Query-Type` header as an explicit equivalent, sent alongside `targetAudience` rather than in place of it. No action is required if you continue using this version.
# Push Overview
Source: https://moengage.com/docs/api/push/push-overview
Create campaigns and send personalized push notifications to individual users or segments across Android, iOS, and Web.
The MoEngage Push API allows you to trigger push notifications. It is designed for high-throughput transactional and marketing use cases, supporting complex targeting logic and deep personalization for every recipient.
Ensure you select the API that corresponds to your specific use case:
* **Transactional:** For system-triggered events (for example, OTPs, status updates), use the Push API.
* **Non-Transactional:** For marketing, promotions, and broadcast messages, MoEngage highly recommends you use the [Push Campaigns API](/docs/api/create-campaigns/create-campaign).
Use this API to:
* **Target Segments:** Send notifications to all users or specific pre-defined segments.
* **Target Individuals:** Reach a single user using unique attributes like Email, Mobile Number, or Unique ID.
* **Personalize at Scale:** Use Jinja templating to dynamically inject user-specific data into the notification payload.
## API Versions
MoEngage introduced v2.1 of the Send Push Notification endpoint to strengthen security and provide more granular rate limiting, including support for IP whitelisting. Two versions of the endpoint are available:
| Version | Endpoint | Notes |
| :----------------- | :-------------------------------------------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **v2.1** (current) | [Push API](/docs/api/push/push-api) | Sends `appId` and `signature` as request headers (`X-MOE-APPKEY` and `X-MOE-PushAPI-Signature`) instead of request-body fields, and adds an `X-MOE-Query-Type` header corresponding to the `targetAudience` field. All other request body and response structure are unchanged from v2. |
| **v2** (legacy) | [Push API (Legacy)](/docs/api/push/push-api-legacy) | The original version of this endpoint. `appId` and `signature` are sent as request-body fields. Fully supported for existing integrations. |
To adapt v2.1, move `appId` and `signature` out of the request body and send them as the `X-MOE-APPKEY` and `X-MOE-PushAPI-Signature` headers. Every other request body field, and the response structure, stays the same. See [Push API](/docs/api/push/push-api) for the full header reference, or [Push API (Legacy)](/docs/api/push/push-api-legacy) for the v2 endpoint details.
## FAQs
No, the MoEngage Push API currently does not support silent (data-only) notifications.
Yes, the basic template supports standard HTML tags for text formatting on supported platforms.
You can provide a `fallback` object within the payload. If the personalization logic fails (e.g., a missing user attribute), the system will automatically send the fallback content instead.
In the `targetUserAttributes` object, set the `attribute` field to `PUSH_ID` and provide the device token as the `attributeValue`.
v2.1 strengthens security and rate limiting, including support for IP whitelisting. It moves `appId` and `signature` from the request body to the `X-MOE-APPKEY` and `X-MOE-PushAPI-Signature` headers, and adds an `X-MOE-Query-Type` header. Every other request body field and the response structure are unchanged from v2. MoEngage recommends v2.1 for all new integrations. The legacy v2 endpoint remains fully supported for existing integrations.
## Postman Collection
We have made it easy for you to test the APIs:
* [v2.1 Postman collection](https://www.postman.com/moengage-dev/api-docs/collection/5ujszpd/moengage-transaction-push-api-v2-1?action=share\&source=copy-link\&creator=5883765)
* [Legacy v2 Postman collection](https://www.postman.com/moengage-dev/api-docs/collection/acmujor/moengage-transaction-push-api-v2?action=share\&source=copy-link\&creator=5883765)
# Rate Limits
Source: https://moengage.com/docs/api/rate-limits
Understand API rate limits and payload size caps for the MoEngage REST APIs, how to monitor usage, and what happens when a limit is exceeded.
MoEngage enforces rate limits on its REST APIs to keep the shared platform fast and reliable for every customer. Limits apply **per workspace** unless a specific endpoint states otherwise (some are per client or per consumer), and a request that exceeds its limit is rejected with an `HTTP 429 Too Many Requests` response.
Most limits below are the defaults. Several limits can be raised for your workspace based on genuine need — contact your MoEngage Customer Success Manager (CSM) or the Support team to discuss an increase.
Rate limits and payload caps are two separate controls. A request can be within the rate limit but still be rejected for exceeding the [payload size](#payload-size-limits), or vice versa. Each endpoint's own reference page carries its authoritative limit; the tables below summarize them.
## Rate limits by endpoint
Endpoints are grouped by product area to match the API reference navigation. When several endpoints share a single pool, the limit is applied across all of them.
### Data
| Endpoint | Rate limit |
| ---------------------------------------------------------------- | -------------------------------------------------------------- |
| `POST /customer/{app_id}` — Track User | 10,000 user updates per minute |
| `POST /customers/export` — Get User | 1,000 users per minute; up to 20 users per payload |
| `POST /customer/merge` — Merge Users | 1,000 user updates per minute; up to 50 users per call |
| `POST /customer/delete` — Delete Users | 5,000 requests per minute; 1 user per request |
| `POST /event/{Workspace_ID}` — Track Event | 30,000 events per minute |
| `POST /device/{app_id}` — Track Device | 10,000 device updates per minute |
| `POST /devices/manage` — Device Opt-out | 1,000 requests per minute |
| `POST /transition/{Workspace_ID}` — Bulk Import Users and Events | 60,000 users and 60,000 events per minute, across all requests |
### File imports
| Endpoint | Rate limit |
| ---------------------------------------------------------------- | -------------------------------------------------------- |
| `POST /fileimports/trigger/{schedule_id}` — Trigger File Imports | Once every 5 minutes per `schedule_id` (else `HTTP 400`) |
| `POST /fileimports/import/status` — Import Details | 50 requests per minute |
| `POST /fileimports/import/run/history` — Import File Run History | 50 requests per minute |
### Business Events
| Endpoint | Rate limit |
| ----------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------- |
| `POST /v5/business-events/triggers` — Trigger Business Event (V5) | Campaign triggers: 10 per 5 minutes, 50 per hour, 200 per day. Flow triggers: 3 per hour, 10 per day |
| `GET /v5/business-events` — List Business Events | 60 requests per minute, 1,000 requests per hour per workspace |
| `POST /v5/business-events/search` — Search Business Events (V5) | 30 requests per minute, 500 requests per hour per workspace |
### Templates and content
| Endpoint | Rate limit |
| ------------------------------------------------------------------------- | ----------------------------------------------------------------------------------- |
| `POST` / `GET` / `PUT /email-templates`, `PUT /bulk/email-templates` (V1) | 1,000 requests per minute per workspace (combined across all V1 template endpoints) |
| `POST /custom-templates/email` — Create Email Template (V2) | 100 requests per minute; up to 10,000 templates per channel |
| `PUT /custom-templates/email`, `POST /custom-templates/email/search` | 100 requests per minute |
| `POST /custom-templates/push` — Create Push Template | 100 requests per minute |
| `PUT /custom-templates/push`, `POST /custom-templates/push/search` | 100 requests per minute |
| `POST /custom-templates/sms` — Create SMS Template | 100 requests per minute; up to 100 templates per channel |
| `PUT /custom-templates/sms`, `POST /custom-templates/sms/search` | 100 requests per minute |
| `POST /custom-templates/osm` — Create OSM Template | 100 requests per minute; up to 100 templates per minute |
| `PUT /custom-templates/osm`, `POST /custom-templates/osm/search` | 100 requests per minute |
### Content APIs
| Endpoint | Rate limit |
| ---------------------------------------------------- | ----------------------------------------------------------- |
| `GET /v5/content-apis` — List Content APIs | 10 requests per minute, 100 requests per hour per workspace |
| `POST /v5/content-apis/{id}/test` — Test Content API | 10 requests per minute, 100 requests per hour per workspace |
### Locales
| Endpoint | Rate limit |
| -------------------------------- | ------------------------------------------------------------- |
| `GET /v5/locales` — List Locales | 60 requests per minute, 1,000 requests per hour per workspace |
### Recommendations
| Endpoint | Rate limit |
| --------------------------------------------------------------------------------- | --------------------------------- |
| `GET /recommendations/{recommendations_id}` — Fetch Recommendation Details | 1,000 recommendations per minute |
| `POST /recommendations/{recommendations_id}/items` — Fetch Recommendation Results | 10,000 recommendations per minute |
### Coupons
| Endpoint | Rate limit |
| ------------------------------------------------------------------------------------ | --------------------------- |
| `POST /coupon-list` — Create a Coupon List | 100 coupon lists per day |
| `GET /coupon-list` — Fetch All Coupon Lists | 10,000 coupon lists per day |
| `GET /coupon-list/{coupon_list_id}` — Fetch Coupon List Details | 10,000 coupon lists per day |
| `PATCH /coupon-list/{coupon_list_id}` — Update a Coupon List | 100 coupon lists per day |
| `PUT /coupon-list/{coupon_list_id}/activate` — Activate Coupon List | 100 coupon lists per day |
| `PUT /coupon-list/{coupon_list_id}/archive` — Archive a Coupon List | 100 coupon lists per day |
| `POST /coupon-list/{coupon_list_id}/files` — Upload a Coupon File | 5 per minute or 50 per day |
| `GET /coupon-list/{coupon_list_id}/files` — Fetch All Coupon Files | 10,000 coupon files per day |
| `GET /coupon-list/{coupon_list_id}/files/{coupon_file_id}` — Fetch a Coupon File | 10,000 coupon files per day |
| `DELETE /coupon-list/{coupon_list_id}/files/{coupon_file_id}` — Delete a Coupon File | 5 per minute or 50 per day |
| `POST /coupon-list/{coupon_list_id}/usage-report` — Generate Usage Report | 5 per minute or 50 per day |
### Catalog
| Endpoint | Rate limit |
| ------------------------------------------------------------- | ------------------------------------------------------------ |
| `POST /catalog` — Create Catalog | 100 per minute or 1,000 per hour |
| `PATCH /catalog/{catalog_id}/attributes` — Add Attributes | 100 per minute or 1,000 per hour |
| `POST /catalog/{catalog_id}/items` — Add Items | 100 per minute or 1,000 per hour; up to 50 items per request |
| `PATCH /catalog/{catalog_id}/items` — Update Items | 100 per minute or 1,000 per hour; up to 50 items per request |
| `POST /catalog/{catalog_id}/items/bulk-delete` — Delete Items | 100 per minute or 1,000 per hour; up to 50 items per request |
| `POST /catalog/{catalog_id}/items/search` — Get Items | 100 per minute or 1,000 per hour; up to 50 items per request |
### Flows
| Endpoint | Rate limit |
| -------------------------------------------------------------------- | ----------------------------------------------------------- |
| `POST /v5/flows/search` — Search Flows | 10 per second, 100 per minute, 6,000 per hour per workspace |
| `GET /v5/flows/{flow_id}` — Get a Single Flow | 10 per second, 100 per minute, 6,000 per hour per workspace |
| `GET /v5/flows/{flow_id}/versions/{version_id}` — Get a Flow Version | 10 per second, 100 per minute, 6,000 per hour per workspace |
| `PATCH /v5/flows/{flow_id}/status` — Update Flow Status | 10 per second, 100 per minute, 6,000 per hour per workspace |
### Offerings
| Endpoint | Rate limit |
| ------------------------------------------------- | -------------------------------------------------------- |
| `GET /v5/offers` — List Offerings | 150 per minute, 500 per hour, 1,000 per day per consumer |
| `POST /v5/offers` — Create Offering | 100 per minute, 300 per hour, 600 per day per consumer |
| `PATCH /v5/offers/{offer_id}` — Update Offering | 100 per minute, 300 per hour, 600 per day per consumer |
| `GET /v5/offers/templates` — List Offer Templates | 150 per minute, 500 per hour, 1,000 per day per consumer |
### Campaigns (V5)
| Endpoint | Rate limit |
| ------------------------------------------------------------------- | ------------------------------------------------- |
| `POST /v5/campaigns` — Create Campaign Draft | 5 per minute, 25 per hour, 100 per day per client |
| `GET /v5/campaigns/{campaign_id}` — Get Campaign | 10 per second, 100 per minute per client |
| `PATCH /v5/campaigns/{campaign_id}` — Update Campaign | 10 per second, 100 per minute per client |
| `POST /v5/campaigns/{campaign_id}/validate` — Validate Campaign | 10 per second, 100 per minute per client |
| `PATCH /v5/campaigns/{campaign_id}/status` — Update Campaign Status | 10 per second, 100 per minute per client |
| `POST /v5/campaigns/search` — Search Campaigns | 10 per second, 100 per minute per client |
| `POST /v5/campaigns/meta` — Get Campaign Meta | 10 per second, 100 per minute per client |
| `POST /v5/campaigns/test` — Test Campaign | 10 per second, 100 per minute per client |
### Campaigns (Legacy)
| Endpoint | Rate limit |
| ----------------------------------------------------------------------- | ------------------------------------------------- |
| `POST /campaigns` — Create Campaign | 5 per minute, 25 per hour, 100 per day per client |
| `PATCH /campaigns/{campaign_id}` — Update Campaign | 10 per second, 100 per minute per client |
| `POST /campaigns/search` — Search Campaigns | 10 per second, 100 per minute per client |
| `POST /campaigns/test` — Test Campaign | 10 per second, 100 per minute per client |
| `POST /campaigns/meta` — Get Campaign Meta | 10 per second, 100 per minute per client |
| `POST /campaigns/status` — Change Campaign Status | 10 per second, 100 per minute per client |
| `POST /campaigns/{parent_campaign_id}/executions` — Get Child Campaigns | 10 per second, 100 per minute per client |
| `POST /personalization/preview` — Personalized Preview | 10,000 requests per minute |
| `PUT /global-control-group/users` — Update Global Control Group | 10 requests per minute |
### Campaign report and stats
| Endpoint | Rate limit |
| ------------------------------------------------------------ | ------------------------------------- |
| `POST /core-services/v1/campaign-stats` — Get Campaign Stats | 100 requests per minute per workspace |
### Analytics
| Endpoint | Rate limit |
| --------------------------------------------------------------------- | ------------------------------------------------------- |
| `POST /v5/analytics/behavior` — Register a Behavior Query | 5 per second, 20 per minute, 350 per hour per workspace |
| `POST /v5/analytics/funnels` — Register a Funnels Query | 5 per second, 20 per minute, 250 per hour per workspace |
| `POST /v5/analytics/retention` — Register a Retention Query | 5 per second, 10 per minute, 50 per hour per workspace |
| `POST /v5/analytics/session-source` — Register a Session-Source Query | 5 per second, 20 per minute, 50 per hour per workspace |
| `POST /v5/analytics/user-analysis` — Register a User Analysis Query | 5 per second, 15 per minute, 100 per hour per workspace |
Analytics queries are also governed by your workspace's monthly Fair Usage Policy (FUP) data-scan quota, which is separate from these rate limits. When the quota is exhausted, analytics queries return `HTTP 428` for the rest of the billing cycle. Contact your Customer Success Manager to expand your quota.
### Segments
| Endpoint | Rate limit |
| ----------------------------------------------------------------- | ----------------------------------------------------------------- |
| `POST /v2/custom-segments/file-segment` — Create File Segment | 10 file-segment operations per hour (create/add/remove, combined) |
| `PUT /v2/custom-segments/file-segment/add-users`, `/remove-users` | Counted within the 10 file-segment operations per hour limit |
| `POST /v3/custom-segments` — Create Filter Segment | 50 per minute, 200 per hour, 1,000 per day |
| `GET /v3/custom-segments` — List Segments | 50 per minute, 200 per hour, 1,000 per day |
| `GET /v3/custom-segments/{id}` — Get Segment by ID | 100 per minute, 1,000 per hour, 4,000 per day |
| `POST /v1/integrations/cohortsync` — Sync Cohort Members | 300 requests per minute |
A workspace can have up to **1,000 active segments** at a time (file, filter, and custom combined).
### Subscriptions
| Endpoint | Rate limit |
| ---------------------------------------------------------------------------------------- | ------------------------------------------------------------------------ |
| `POST /emails/v1.0/bulk-resubscribe` — Bulk Resubscribe Users | 10 per minute; 14,400 per day; up to 100 recipient addresses per request |
| `PUT /v1.0/opt-in-management/user-preferences` — Update Email Opt-in Preferences | 1,000 per minute; 360,000 per day |
| `GET` / `PUT` / `POST /category-subscription/user-preferences` — Subscription Categories | 100 per minute; 360,000 per day (each) |
### Push
| Endpoint | Rate limit |
| ----------------------------------------------------------------- | -------------------------- |
| `POST /transaction/sendpush` — Send Push Notification | 10,000 requests per minute |
| `POST /v2.1/transaction/sendpush` — Send Push Notification (V2.1) | 10,000 requests per minute |
### Cards
| Endpoint | Rate limit |
| ---------------------------------------------- | ---------------------------------------- |
| `POST /cards/fetch` — Fetch Cards for User | 50,000 requests per minute per workspace |
| `DELETE /cards/delete` — Delete Cards for User | 50,000 requests per minute per workspace |
### Inform
| Endpoint | Rate limit |
| ---------------------------------------------- | -------------------------- |
| `POST /alerts/send` — Send Transactional Alert | 10,000 requests per minute |
### Live Activities
| Endpoint | Rate limit |
| --------------------------------------------------------- | --------------------------------------------------------------- |
| `POST /live-activity/broadcast/start` — Start Broadcast | 5 per minute per workspace |
| `POST /live-activity/broadcast/update` — Update Broadcast | 500 per minute per workspace; 5 per minute per live-activity ID |
| `POST /live-activity/broadcast/end` — End Broadcast | 500 per minute per workspace; 5 per minute per live-activity ID |
### Personalize
| Endpoint | Rate limit |
| -------------------------------------------- | --------------------------------------------------------------------------------------------- |
| `POST /experiences/fetch` — Fetch Experience | 10,000 requests per minute per workspace (configurable on request; may incur additional cost) |
### Message Archival
| Endpoint | Rate limit |
| --------------------------------------------- | ------------------------- |
| `POST /archival/view` — View Archived Message | 1,000 requests per minute |
## Payload size limits
Payload caps are enforced independently of rate limits. A request larger than the cap is rejected — typically with `HTTP 413` (payload too large) or `HTTP 400`, depending on the endpoint.
| API / endpoint | Maximum payload |
| -------------------------------------------------------- | ------------------------------------------ |
| Track User (`/customer/{app_id}`) | 128 KB per request |
| Merge Users (`/customer/merge`) | 128 KB per request |
| Cohort sync (`/v1/integrations/cohortsync`) | 128 KB per request |
| GDPR / CCPA (`/opengdpr_requests/{appId}`) | 128 KB per request; 100 KB per user record |
| Custom Segments — File | 150 MB per file |
| Catalog API | 5 MB per request |
| Recommendations API | 1 MB per request |
| Live Activities (`/live-activity/broadcast/*`) | 5,120 bytes per request (iOS) |
| Email bulk resubscribe (`/emails/v1.0/bulk-resubscribe`) | 100 recipient addresses per request |
# Fetch All Recommendations
Source: https://moengage.com/docs/api/recommendations/fetch-all-recommendations
/api/recommendations/recommendations.yaml get /recommendations
This API retrieves the list of recommendations configured in your workspace, along with the metadata of each recommendation. The results are sorted by the last updated time, with the most recently updated recommendation first.
Use this API to look up the `recommendation_id` of a recommendation, which you need for the [Fetch Recommendation Details](/api/recommendations/fetch-recommendation-details) and [Fetch Recommendation Results](/api/recommendations/fetch-recommendation-results) APIs.
The response does not include a total count of recommendations. You have reached the last page when the `items` array contains fewer recommendations than the `size` you requested, or when it is empty.
#### Rate Limit
You can make 100 requests per minute and 10,000 requests per day.
# Fetch Recommendation Details
Source: https://moengage.com/docs/api/recommendations/fetch-recommendation-details
/api/recommendations/recommendations.yaml get /recommendations/{recommendation_id}
This API retrieves the metadata associated with a specific recommendation setup using its unique ID. The metadata can include the recommendation name, model type, status, creation and update time, and so on.
#### Rate Limit
You can make 100 requests per minute and 10,000 requests per day.
# Fetch Recommendation Results
Source: https://moengage.com/docs/api/recommendations/fetch-recommendation-results
/api/recommendations/recommendations.yaml post /recommendations/{recommendation_id}/items
This API fetches/retrieves the metadata results of any recommendations for a user based on their user ID and item ID.
#### Rate Limit
You can make 10,000 requests per minute.
# Recommendations Overview
Source: https://moengage.com/docs/api/recommendations/recommendations-overview
Fetch recommendation metadata and retrieve personalized item results.
The MoEngage Recommendation API allows you to interact with your recommendation engines. You can retrieve the configuration metadata of a specific recommendation setup or fetch the actual recommended items for a specific user based on various models like "Similar Items," "Frequently Bought Together," or "Trending."
If this API is not enabled for your account, contact your MoEngage Customer Success Manager (CSM) or the Support team to request enablement.
## Endpoints
The Recommendation API is a collection of the following endpoints:
* [Fetch All Recommendations](/docs/api/recommendations/fetch-all-recommendations): Lists the recommendations in your workspace, with pagination and status filters.
* [Fetch Recommendation Details](/docs/api/recommendations/fetch-recommendation-details): Fetches configuration details, status, and logic.
* [Fetch Recommendation Results](/docs/api/recommendations/fetch-recommendation-results): Fetches personalized items for a user.
## FAQs
### General Recommendation Questions
The `recommendation_id` is a unique identifier generated when you create a recommendation in the MoEngage Recommendation module. You can find this ID on the individual recommendation's overview page in the MoEngage Dashboard, or by calling [Fetch All Recommendations](/docs/api/recommendations/fetch-all-recommendations).
Fetch Recommendation Details and Fetch Recommendation Results both return `400` for any `recommendation_id` they cannot serve. A recommendation that was archived and a recommendation that was never created both return `400`. Check the `recommendation_id` and the recommendation's status in the MoEngage dashboard.
By default, the API returns all attributes associated with an item in the catalog. For catalogs with many attributes, this can significantly increase response size and latency. Specifying only the fields you need (e.g., `["title", "price", "image_link"]`) improves performance.
### Fetch Results
The `item_id` acts as an anchor for collaborative filtering models. It is mandatory when the `RECOMMENDATION-TYPE` is set to `similar_item`, `frequently_viewed_together`, or `frequently_bought_together`.
The API requires a valid `user_id`. If the user is unknown or hasn't had any interactions recorded, the recommendation engine may return default items (like trending items) or an empty list depending on your "fallback" settings in the dashboard.
The API will return a `413 Request Entity Too Large` error. This usually happens if the `include_fields` list is excessively long or if a very high number of items are requested (if applicable).
## Postman Collections
Test the Recommendation APIs immediately using our Postman collection. [View Postman Collection](https://www.postman.com/moengage-dev/api-docs/collection/35sr4cv/moengage-recommendation-public-api)
# Generate Usage Report
Source: https://moengage.com/docs/api/reports/generate-usage-report
/api/coupons/coupons.yaml post /coupon-list/{coupon_list_id}/usage-report
This API produces a detailed usage report for a specific coupon list, providing details on which user received which coupon from which locale or variation of which campaign at what time. After it is generated, this report is delivered directly to the requested email addresses. Using this API, you can conduct a comprehensive analysis of critical data and coupon usage trends efficiently.
#### Rate Limit
You can generate:
* 5 usage reports of coupon list per minute or
* 50 usage reports of coupon list per day
# Bulk Resubscribe Users
Source: https://moengage.com/docs/api/resubscribe/bulk-resubscribe-users
/api/email-subscription/email-subscription.yaml post /emails/v1.0/bulk-resubscribe
This API resubscribes users who have previously unsubscribed on the MoEngage platform and an external email vendor platform simultaneously. This API resets the unsubscribe flag to “false” for users on MoEngage and makes a call to an External Service Provider (ESP) like SendGrid to remove the email addresses (associated with the unsubscribed users) from their suppression list.
#### Supported ESPs
The Resubscription API currently supports only **SendGrid**.
#### Rate Limit
The rate limit is **10 RPM** (requests per minute). The allowed volume is **14.4K** requests per day, with a maximum payload size of **100** recipient email addresses per request.
# Create SMS Template
Source: https://moengage.com/docs/api/sms-templates/create-sms-template
/api/sms-templates/sms-templates.yaml post /custom-templates/sms
This API creates an SMS template in MoEngage. It helps you upload templates created outside the MoEngage ecosystem to MoEngage and use them for campaign creation.
#### Rate Limit
The rate limit is **100 RPM**. You can upload a maximum of **100 templates per channel**.
# Search SMS Templates
Source: https://moengage.com/docs/api/sms-templates/search-sms-templates
/api/sms-templates/sms-templates.yaml post /custom-templates/sms/search
This API searches and retrieves a list of SMS templates, created in your MoEngage account, based on specified filter criteria.
**Mandatory Pagination Update**
We are introducing mandatory pagination, effective **November 15, 2025**, all calls to this API must include the following two parameters:
* `page`: The page number of the results you wish to fetch.
* `entries`: The number of templates to return per page, with a maximum value of **15**.
Please update all integrations to include these parameters. API requests submitted without them after the effective date will result in an error and fail to execute.
#### Rate Limit
The rate limit is **100 RPM**.
# SMS Templates Overview
Source: https://moengage.com/docs/api/sms-templates/sms-templates-overview
Use the MoEngage SMS Templates API to create, search, and update reusable SMS templates for campaigns.
The MoEngage SMS Template API allows marketers define, reuse, update, and manage SMS templates created outside the MoEngage ecosystem effortlessly. You can create multiple versions of the same template and mark whether they can be used in the campaigns that are active currently. Users can create templates using the Create SMS Template API and update them using the Update SMS Template API or edit them in the MoEngage dashboard (provided they have the specific editing permissions for the templates allowed for their role).
## Endpoints
The SMS Template API is a collection of the following endpoints:
* [Create SMS Template](/docs/api/sms-templates/create-sms-template): Creates an SMS template.
* [Update SMS Template](/docs/api/sms-templates/update-sms-template): Updates an SMS template specified by its Template ID.
* [Search SMS Template](/docs/api/sms-templates/search-sms-templates): Fetches an SMS template using its Template ID or other filters like template name, template version, and so on. It can also list all the SMS templates created in MoEngage.
## FAQs
### Create SMS Template
Yes, you can create multiple templates with the same name, provided they have different versions.
## Postman Collection
Test these endpoints quickly by importing our Postman collection: [**View in Postman**](https://www.postman.com/moengage-dev/api-docs/collection/n0lb9wd/moengage-custom-templates-api?action=share\&creator=3182294)
# Update SMS Template
Source: https://moengage.com/docs/api/sms-templates/update-sms-template
/api/sms-templates/sms-templates.yaml put /custom-templates/sms
This API updates an SMS template by specifying its external template ID. You can specify in the request whether the updated version of the template can be used in active campaigns.
#### Rate Limit
The rate limit is **100 RPM**.
# Campaign Stats and Reports Overview
Source: https://moengage.com/docs/api/stats-report/stats-report-overview
Fetch real-time campaign performance statistics and download detailed campaign reports programmatically.
The MoEngage Campaign Stats and Reports API allows you to monitor your marketing performance outside of the MoEngage dashboard. Use these endpoints to retrieve real-time platform-level statistics or generate downloadable reports for long-term analysis.
## Endpoints
The Campaign Stats and Reports API is divided into two primary functional areas:
* [Get Campaign Stats](/docs/api/stats/get-campaign-stats): Retrieves real-time performance metrics (clicks, impressions, conversions) at a platform level.
* [Download Campaign Report](/docs/api/campaign-reports/download-campaign-report): Directly downloads campaign data files for specified date ranges.
## Comparison of Capabilities
| Feature | Campaign Stats API | Campaign Report API |
| ----------------- | -------------------------- | ------------------------- |
| **Data Recency** | Real-time | Historical (Date Range) |
| **Output Format** | JSON | ZIP/GZIP File |
| **Batch Limit** | 10 Campaigns per call | 90 Days date range |
| **Primary Use** | Live Monitoring/Dashboards | Deep-dive Analysis/Audits |
## FAQs
### Campaign Statistics
You can pass up to **10 campaign IDs** in the `campaign_ids` array per API call. If you need data for more campaigns, utilize the `offset` and `limit` parameters to paginate through your results.
The `metric_type` parameter allows you to toggle your view. **TOTAL** counts every occurrence (e.g., three clicks by one user), while **UNIQUE** counts the number of distinct users who performed the action.
Yes. The response object is nested by Platform > Locale > Variation, allowing you to see granular performance for A/B tests and localized content.
If only the start date is passed without the end date, the API will return an error response. Both the start and end dates are required to retrieve the desired response.
When providing the start and end dates without any campaign ID, the API will return the stats for both the parent and child campaigns. The stats will be available for the specified time period.
To identify the stats belonging to the parent campaign in the API response, you can use the "Campaign Details and Reachability" API. This API should be called with all the campaign IDs included in the request. The parent campaign ID field will not be available for the parent campaign, allowing you to distinguish it from the child campaigns.
Campaign Details and Reachability APIs will allow you to relate child campaigns to their parent campaigns.
### Campaign Reports
This typically happens if the `FILENAME` provided is incorrect, the report was created more than 7 days ago (expired), or the `APP_ID` in the path does not match your credentials.
Yes, the Report API supports both one-time and periodic campaigns. Ensure the filename matches the specific instance you wish to download.
Verify that your concatenation uses the pipe character (`|`) exactly as shown: `APPID|FILENAME|SECRETKEY`. Ensure the resulting string is encoded in UTF-8 before hashing with SHA256.
Campaigns might not be available to run on the selected date to fetch the report, which could result in an error message.
The reports generated will expire in 7 days from the date of creation.
You can generate reports for up to 90 days.
## Postman Collections
Test your integration and verify your signature logic using our Postman collection. [View Postman Collection](https://www.postman.com/moengage-dev/api-docs/collection/s8t1ta0/moengage-campaign-stats-api) →
# Get Campaign Stats
Source: https://moengage.com/docs/api/stats/get-campaign-stats
/api/stats-report/stats-report.yaml post /core-services/v1/campaign-stats
This API fetches data at the platform level and provides data for all types of campaigns.
#### Rate Limit
This API provides all version stats, and the call rate is limited to **100 API calls per minute** per workspace.
* Make sure to pass the relevant Campaign IDs in the request to fetch stats for flow-linked campaigns.
* Stats data is retained for the entire lifetime of a campaign, from its start date, there is no fixed retention cutoff. The 30-day limit applies only to the date range of a single API call, not to how far back you can query.
# Subscription Categories Overview
Source: https://moengage.com/docs/api/subscription-categories/subscription-categories-overview
Fetch and update email subscription preferences on your MoEngage dashboard.
The MoEngage Subscription Categories API allows you to manage and synchronize user communication preferences across your systems and MoEngage. These endpoints support fetching individual settings, updating preferences via campaign interactions, and performing high-volume bulk updates. For more information on Subscription categories, refer to [Subscription Categories](/docs/user-guide/campaigns-and-channels/email/getting-started-with-email/configure-subscription-categories).
## Endpoints
The Subscription Categories API consists of the following endpoints:
* [Get Subscription Preferences](/docs/api/subscription-preferences/get-subscription-preferences): Fetches the subscription category preferences information for a specific user.
* [Update Subscription Preferences](/docs/api/subscription-preferences/update-subscription-preferences): Updates the subscription category preferences information for a specific user who navigates to the custom landing page from the email notification sent to them and updates their preferences.
* [Bulk Update Subscription Preferences](https://moengage.mintlify.app/api/subscription-preferences/bulk-update-subscription-preferences): Updates subscription category preferences in bulk. Use this API for updating the user preferences to MoEngage in large volumes.
## FAQs
### Get Subscription Preferences
The user\_id (MoEngage ID) and the cid (Campaign ID) fields should be fetched from the landing page URL. For example, if the link to the custom landing page was `https://www.abc.com/managepreference`, when the user clicks the same from the email, the link will be as below:
```json theme={null}
https://www.abc.com/managepreference?user_id=7XvJW2dj3iS.rYAt4pg5ASBQtaqAMFDw9e89vZCXx_RFfN3eL0wBG008oI6cpncOQV6ESg&cid=5FGZGcA8FRv3Id89JCCczQjwxq6ApEyAarYDje2mzfRQ_WG7VyyFEc0w4L3MA.31a7wzR64fi7lfq8Km0AaeO4paGVul.4HxixDYnoUp21xyUjGtfvnrHSR2G1reTpYPHHU.r3Ac9vE&app_key=UY_GHXBXyaHbiYnCoNOu7eS.u5yIrrN3noTBNZX5mNSL4KN5C5PZ3zcXBKCCxl9w3m0ukw
```
Here, you can parse and find user\_id and cid
* user\_id is: 7XvJW2dj3iS.rYAt4pg5ASBQtaqAMFDw9e89vZCXx\_RFfN3eL0wBG008oI6cpncOQV6ESg
* cid is: 5FGZGcA8FRv3Id89JCCczQjwxq6ApEyAarYDje2mzfRQ\_WG7VyyFEc0w4L3MA.31a7wzR64fi7lfq8Km0AaeO4paGVul.4HxixDYnoUp21xyUjGtfvnrHSR2G1reTpYPHHU.r3Ac9vE and pass the same while triggering the API.
### Update Subscription Preferences
The user\_id (MoEngage ID) and the cid (Campaign ID) fields should be fetched from the landing page URL. For example, if the link to the custom landing page was `https://www.abc.com/managepreference`, when the user clicks the same from the email, the link will be as below:
```json theme={null}
https://www.abc.com/managepreference?user_id=7XvJW2dj3iS.rYAt4pg5ASBQtaqAMFDw9e89vZCXx_RFfN3eL0wBG008oI6cpncOQV6ESg&cid=5FGZGcA8FRv3Id89JCCczQjwxq6ApEyAarYDje2mzfRQ_WG7VyyFEc0w4L3MA.31a7wzR64fi7lfq8Km0AaeO4paGVul.4HxixDYnoUp21xyUjGtfvnrHSR2G1reTpYPHHU.r3Ac9vE&app_key=UY_GHXBXyaHbiYnCoNOu7eS.u5yIrrN3noTBNZX5mNSL4KN5C5PZ3zcXBKCCxl9w3m0ukw
```
Here, you can parse and find user\_id and cid
* user\_id is: 7XvJW2dj3iS.rYAt4pg5ASBQtaqAMFDw9e89vZCXx\_RFfN3eL0wBG008oI6cpncOQV6ESg
* cid is: 5FGZGcA8FRv3Id89JCCczQjwxq6ApEyAarYDje2mzfRQ\_WG7VyyFEc0w4L3MA.31a7wzR64fi7lfq8Km0AaeO4paGVul.4HxixDYnoUp21xyUjGtfvnrHSR2G1reTpYPHHU.r3Ac9vE and pass the same while triggering the API.
### Bulk Update Subscription Preferences
The maximum batch size is 50.
## Postman Collection
Test these endpoints quickly by importing our Postman collection: [**View in Postman**](https://www.postman.com/moengage-dev/api-docs/collection/30vndhm/moengage-subscription-categories-api)
# Bulk Update Subscription Preferences
Source: https://moengage.com/docs/api/subscription-preferences/bulk-update-subscription-preferences
/api/subscription-categories/subscription-categories.yaml post /category-subscription/user-preferences
This API updates subscription category preferences in bulk. You can use this API to update the user preferences to MoEngage in large volumes.
#### Rate Limit
The rate limit is 100 RPM and 360k per day.
# Get Subscription Preferences
Source: https://moengage.com/docs/api/subscription-preferences/get-subscription-preferences
/api/subscription-categories/subscription-categories.yaml get /category-subscription/user-preferences
This API fetches the subscription category preferences information for a specific user based on the encrypted User ID and Campaign ID found in the landing page URL.
#### Rate Limit
The rate limit is 100 RPM and 360k per day.
# Update Subscription Preferences
Source: https://moengage.com/docs/api/subscription-preferences/update-subscription-preferences
/api/subscription-categories/subscription-categories.yaml put /category-subscription/user-preferences
This API updates the subscription category preferences for a specific user who navigates from an email. This endpoint requires the encrypted IDs obtained from the email link.
#### Rate Limit
The rate limit is 100 RPM and 360k per day.
# Create Push Template
Source: https://moengage.com/docs/api/templates/create-push-template
/api/push-templates/push-templates.yaml post /custom-templates/push
This API creates a new push notification template for one or more platforms (Android, iOS).
#### Rate Limit
The rate limit is 100 RPM (Requests Per Minute).
# Search for Push Templates
Source: https://moengage.com/docs/api/templates/search-for-push-templates
/api/push-templates/push-templates.yaml post /custom-templates/push/search
This API searches the push templates created in your MoEngage account.
We are introducing mandatory pagination, effective November 15, 2025, all calls to this API must include the following two parameters:
* **page**: The page number of the results you wish to fetch.
* **entries**: The number of templates to return per page, with a maximum value of 15.
Please update all integrations to include these parameters. API requests submitted without them after the effective date will result in an error and fail to execute.
#### Rate Limit
The rate limit is 100 RPM (request per minute).
# Update Push Template
Source: https://moengage.com/docs/api/templates/update-push-template
/api/push-templates/push-templates.yaml put /custom-templates/push
This API updates an existing push notification template by creating a new version.
#### Rate Limit
The rate limit is 100 RPM (Requests Per Minute).
# Personalized Preview
Source: https://moengage.com/docs/api/test-campaigns/personalized-preview
/api/campaigns/campaigns.yaml post /personalization/preview
This API shows a preview of personalized content for a specific user before sending a Push, Email, or SMS campaign. This API retrieves personalized message content with all user/event attributes resolved. Use this API to validate your personalization logic and see exactly how content will appear to specific users before launching campaigns.
#### Personalization Support
* User attributes
* Event attributes
* Custom templates
* Content blocks
* Content APIs
* Product sets
#### Rate Limit
The rate limit is 10,000 requests per minute.
# Test Campaign
Source: https://moengage.com/docs/api/test-campaigns/test-campaign
/api/campaigns/campaigns.yaml post /campaigns/test
This API sends a test Push or Email campaign to specific users or identifiers before launching it to your entire audience. You can only test campaigns created via the API, not campaigns created through the MoEngage dashboard.
#### Rate Limits
| Rate Limit Name | Rate Limit |
| :----------------------- | :------------------------------------------------------------------------------- |
| Test campaign per second | The total number of test campaign requests per second per client allowed is 10. |
| Test campaign per minute | The total number of test campaign requests per minute per client allowed is 100. |
| Test campaign per hour | The total number of test campaign requests per hour per client allowed is 6000. |
**Notes**
* Breaching the limits will reject the request.
* Per hour and per day limits will consider the calculation based on the last hour and last 24 hrs respectively.
# Test Campaign (V5)
Source: https://moengage.com/docs/api/test-campaigns/test-campaign-v5
/api/campaigns/campaign-draft.yaml post /v5/campaigns/test
Sends a test Push or Email message to specific users or device identifiers before publishing the campaign.
The endpoint supports two modes:
**Inline mode**
* Send `channel` and `campaign_content` in the request.
* For **EMAIL** campaigns, also include `basic_details` and `connector`.
* Nothing is stored on the server.
**Draft mode**
* Send `draft_id` to load content from a saved **DRAFT**.
* By default, the server sends one test per platform, locale, and variation.
* To narrow the send, use `test_campaign_meta.platform`, `locale_name`, or `variation`.
#### Rate Limits
| Rate Limit Name | Rate Limit |
| :----------------------- | :------------------------------------------------------------------------------- |
| Test campaign per second | The total number of test campaign requests per second per client allowed is 10. |
| Test campaign per minute | The total number of test campaign requests per minute per client allowed is 100. |
| Test campaign per hour | The total number of test campaign requests per hour per client allowed is 6000. |
# Track App Install
Source: https://moengage.com/docs/api/tracking/track-app-install
/api/data/data.yaml get /installInfo
This API tracks the install attribution data in MoEngage, which you can then use to enhance your marketing automation campaigns on MoEngage.
Install attribution tracking is a great way to improve your initial relationship with your user. Knowing how, where, and even more importantly, why a user installs your app allows you to get a better understanding of who your user is and how you should introduce them to your app.
# Send Transactional Alert
Source: https://moengage.com/docs/api/transactional-alerts/send-transactional-alert
/api/inform/inform.yaml post /alerts/send
This API is used to trigger a transactional message/alert to the user via one or more configured channels.
#### Rate Limit
The default rate limit is 10K RPM.
# Change Campaign Status
Source: https://moengage.com/docs/api/update-campaigns/change-campaign-status
/api/campaigns/campaigns.yaml post /campaigns/status
This API updates the status of campaigns to stop, pause, or resume them. You can only change the status of campaigns created via the [Create Campaign API](https://www.moengage.com/docs/api/create-campaigns/create-campaign) (not dashboard-created campaigns).
**Differences in V5**
`PATCH /v5/campaigns/{campaign_id}/status` accepts one campaign ID as a path parameter per request. V1 accepts a `campaign_ids` array of up to 10 IDs per request. To perform bulk status changes in V5, call the endpoint once per campaign ID.
See [Update Campaign Status (V5)](/docs/api/update-campaigns/update-campaign-status-v5).
Currently, you can use this API to change the status of Email and Push campaigns. You can update the following statuses of the campaigns:
* Stop a scheduled One-time campaign (Email and Push)
* Pause and resume a running Periodic or Event-triggered Email campaign.
* Pause and resume the following running Push campaigns:
* Periodic
* Event-triggered
* Device-triggered
* Location-triggered
#### Rate Limits
| Rate Limit Name | Rate Limit |
| :-------------------------------- | :---------------------------------------------------------------------------------------- |
| change campaign status per second | The total number of change campaign status requests per second per client is 10. |
| change campaign status per minute | The total number of change campaign status requests per minute per client allowed is 100. |
| change campaign statusper hour | The total number of change campaign status requests per hour per client allowed is 6000. |
**Notes**
* Breaching the limits will reject the request.
* Per hour and per day limits will consider the calculation based on the last hour and last 24 hrs respectively.
# Update Campaign Status (V5)
Source: https://moengage.com/docs/api/update-campaigns/update-campaign-status-v5
/api/campaigns/campaign-draft.yaml patch /v5/campaigns/{campaign_id}/status
Applies a state transition to a published campaign. Supported for **Email** and **Push** campaigns.
This endpoint only handles post-publish lifecycle transitions (STOP, PAUSE, RESUME) for campaigns that are already live. Campaign publishing is not yet supported in V5.
The response does not include the resulting campaign status. To confirm the new state after a transition, call `GET /v5/campaigns/{campaign_id}`.
#### Rate Limits
| Rate Limit Name | Rate Limit |
| :-------------------------------- | :---------------------------------------------------------------------------------------- |
| change campaign status per second | The total number of change campaign status requests per second per client is 10. |
| change campaign status per minute | The total number of change campaign status requests per minute per client allowed is 100. |
| change campaign status per hour | The total number of change campaign status requests per hour per client allowed is 6000. |
**Notes**
* Breaching the limits will reject the request.
* Per-hour and per-day limits use a rolling window of the last 1 hour and last 24 hours respectively.
# Update Campaign (V1 — Legacy)
Source: https://moengage.com/docs/api/update-campaigns/update-campaign-v1-—-legacy
/api/campaigns/campaigns.yaml patch /campaigns/{campaign_id}
This API updates an existing Push or Email campaign in MoEngage. You can only update campaigns created via the API, not campaigns created through the MoEngage dashboard.
**Differences in V5**
`PATCH /v5/campaigns/{campaign_id}` returns `200 OK` with a response body containing `response_id`, `type`, and `data.id`. V1 returns `204 No Content` with no response body on success. Update any integration that asserts a `204` status code before adopting V5.
See [Update Campaign (V5)](/docs/api/update-campaigns/update-campaign-v5).
**Update Restrictions**
You cannot update campaigns when in **Stopped** or **Archived** state.
For **Scheduled Campaigns**
* You can edit all fields **except scheduling type** for One-Time campaigns.
* You can edit all fields **except scheduling type** for Periodic/Event-Triggered campaigns if no instance has been sent yet.
For **Active Campaigns**, you cannot edit the following fields:
* Trigger Condition
* Segmentation Details
* Conversion Goal Details
* Scheduling Type
* Scheduling Start Date
For **Event-Triggered campaigns**, updated content is cached and takes up to 30 minutes to take effect.
#### Rate Limits
| Rate Limit Name | Rate Limit |
| :------------------------- | :--------------------------------------------------------------------------------- |
| Update campaign per second | The total number of update campaign requests per second per client allowed is 10. |
| Update campaign per minute | The total number of update campaign requests per minute per client allowed is 100. |
| Update campaign per hour | The total number of update campaign requests per hour per client allowed is 6000. |
**Notes**
* Breaching the limits will reject the request.
* Per hour and per day limits will consider the calculation based on the last hour and last 24 hrs respectively.
# Update Campaign (V5)
Source: https://moengage.com/docs/api/update-campaigns/update-campaign-v5
/api/campaigns/campaign-draft.yaml patch /v5/campaigns/{campaign_id}
Updates individual components of a campaign draft.
**Campaign publishing is not yet available in V5.** This endpoint only supports updating draft components. To publish campaigns, use the V1 API at `PATCH /core-services/v1/campaigns/{campaign_id}` in the interim. Publishing via V5 will be available in a future release.
Send a `PATCH` request without a `status` key in the body.
The API merges each submitted component into the existing draft, then validates the full merged state (`DRAFT_PATCH`).
After all individual components pass validation, cross-component checks run on the combined result.
Only the fields you include in the request body are updated. Fields you omit retain their current values.
For the full field reference per delivery type, see the [Create Campaign API](/docs/api/campaigns/create-campaign).
#### Update restrictions by campaign state
| Campaign state | Editable fields | Non-editable fields |
| :------------------------------------------------------------ | :--------------------------------------------- | :----------------------------------------------------------------------------------------------------------------- |
| **DRAFT** | All fields | — |
| **SCHEDULED:** One-time (no instance sent) | All fields | `scheduling_type` |
| **SCHEDULED:** Periodic or Event-triggered (no instance sent) | All fields | `scheduling_type` |
| **ACTIVE** | All fields except those listed as non-editable | `trigger_condition`, `segmentation_details`, `conversion_goal_details`, `scheduling_type`, `scheduling_start_date` |
| **STOPPED** or **ARCHIVED** | — | All fields (no updates allowed) |
For **Event-triggered campaigns**, updated content is cached and takes up to 30 minutes to take effect.
#### Rate Limits
| Rate Limit Name | Rate Limit |
| :------------------------- | :--------------------------------------------------------------------------------- |
| Update campaign per second | The total number of update campaign requests per second per client allowed is 10. |
| Update campaign per minute | The total number of update campaign requests per minute per client allowed is 100. |
| Update campaign per hour | The total number of update campaign requests per hour per client allowed is 6000. |
**Notes**
* Breaching the limits will reject the request.
* Per-hour and per-day limits use a rolling window of the last 1 hour and last 24 hours respectively.
# Update Global Control Group
Source: https://moengage.com/docs/api/update-campaigns/update-global-control-group
/api/campaigns/campaigns.yaml put /global-control-group/users
This API adds or removes users from the Global Control Group (GCG) in MoEngage. Provide a publicly accessible CSV of user IDs and specify whether to add or remove those users.
**Not available in V5**
This endpoint is not yet available in V5. Use this V1 endpoint at `PUT /core-services/v1/global-control-group/users` until V5 support is added.
#### Rate Limit
The rate limit is 10 RPM.
* One file-processing request must complete before the next API call is accepted.
* Currently, only publicly accessible Amazon S3 URLs are supported for `file_url`.
* The Global Control Group must already be initialized with the **Upload Users** option before this API can be used. This API does not support the **Random allocation** option.
* When the GCG base is successfully updated, subsequent runs of existing campaigns use the updated list.
* Once file processing is completed, an email is sent to the user identified by `updated_by` summarizing the number of users successfully processed, the number of users that failed, and the possible reasons.
# Delete Users
Source: https://moengage.com/docs/api/user/delete-users
/api/data/data.yaml post /customer/delete
This API deletes users in MoEngage. You cannot retrieve users once deleted. Users deleted (hard delete) using this API will be deleted after a default buffer of 24 hours. During this buffer period, the user will still be active in MoEngage and will be visible in Segments, Analytics, and Campaigns. You can update users in the buffer period. After the buffer elapses, the user is hard-deleted from MoEngage. If you create a user with the same unique identifiers as the deleted one in MoEngage (through APIs or imports), they will be created again in MoEngage.
#### Rate Limit
The rate limit is 1 user per payload per request. You can run 5000 requests per minute.
# Get User
Source: https://moengage.com/docs/api/user/get-user
/api/data/data.yaml post /customers/export
This API facilitates the retrieval of information of users by specifying the user IDs.
* Access to Get User is gated because the endpoint can return PII. To enable it for your workspace, contact your CSM or raise a ticket with MoEngage Support. The request creates an audit trail of who asked for access. Workspaces already using the API before gating was introduced were enabled automatically.
* Get User is a **single-lookup** API, you pass one or more identifiers and get back the matching user profile(s), up to the payload limit below. It does not support a "retrieve all users" mode; there's no parameter on this endpoint to export your entire user base. If you need to export all users, use a separate bulk/file export flow instead.
* You can optionally IP-whitelist the callers permitted to invoke this endpoint.
#### Rate Limit
The rate limit is 20 users per payload and 1000 users per minute.
# Merge Users
Source: https://moengage.com/docs/api/user/merge-users
/api/data/data.yaml post /customer/merge
This API merges two users in MoEngage based on their ID, which is a client-defined identifier for a user. You can use this API when multiple profiles have been created for a single user. For example, you can merge a user registered once with a mobile number and once with an email ID. You can also merge duplicate users created due to integration or tech issues.
#### Types of user merging in MoEngage
* Default or normal merge:
* MoEngage merges users with the same ID.
* Happens automatically, and no action is required from your side.
* Manual merge:
* MoEngage merges users having different IDs.
* Does not happen automatically; you must call the Merge User API with the list of users to be merged along with their IDs.
- User Merging is a complex functionality and, if misused, can lead to data integrity issues. If the data passed to the API is incorrect, resulting in a merge of two unintended users, MoEngage will not be able to recover/rectify the data. The retained user would have erroneous data, and segmentation queries would not provide the right results.
- MoEngage does not support transitive/canonical merging. For example, if user A is merged to B (A ->B) and then user B is merged to C (B ->C), in this scenario, events of user A are not moved to user C.
- The Merge User API is not functional in workspaces where the [Identity Resolution](/docs/user-guide/data/user-data/unified-identity-identity-resolution) feature is enabled.
- If you are updating the [Unique Identifier](/docs/developer-guide/unity-sdk/data-tracking/tracking-user-attributes) for a user, use the Merge User API at least 2 hours after you have updated the Unique Identifier.
- Ensure that the data passed to the API is accurate. We recommend you test the merging starting with a small batch of users, such as 1, 5, 10, 20, 50, etc. Verify the merged data and users before proceeding with a bulk update.
- A maximum of 50 users can be merged in a single call.
- Payload size should not exceed 128 KB.
#### Rate limit
The rate limit is 1000 user updates per minute.
# Track User
Source: https://moengage.com/docs/api/user/track-user
/api/data/data.yaml post /customer/{app_id}
This API adds or updates users and user properties in MoEngage. You can create a new user, create new user property, or update existing user properties of users.
* For more information about trackable user attributes, reserved keys, and general data information, refer to the [Data Overview](https://www.moengage.com/docs/api/data/data-overview).
* If you have [Portfolio](/docs/user-guide/settings/account/portfolio/portfolio) enabled for your workspace, you must pass `project_code` in the API endpoint. This identifies which project a user or event belongs to. For more information, refer to [Portfolio: Data Ingestion and Management](/docs/user-guide/data/key-concepts/portfolio-data-ingestion-and-management).
* [Unsetting Attributes](/docs/user-guide/data/user-data/unset-user-attributes) is an Early Access feature. To enable it for your account, contact your MoEngage Customer Success Manager (CSM) or the Support team.
#### Rate Limit
A single API request contains one or more user updates. Maintain a rate limit of 10,000 user updates per minute.
# Test Connection API
Source: https://moengage.com/docs/api/utilities/test-connection-api
/api/data/data.yaml post /integrations/authentication
This API validates if the entered endpoint details are valid. It verifies if the provided endpoint URL, workspace ID, and data key are accessible and responds without any errors.
# Android TV
Source: https://moengage.com/docs/developer-guide/android-sdk/android-tv/android-tv
Integrate MoEngage features like data tracking, in-app messages, and cards into your Android TV app.
MoEngage supports your apps available on Android TV.
Ensure that the [Android SDK integration](/docs/developer-guide/android-sdk/sdk-integration/basic-integration/sdk-initialization) is completed.
# Supported Features
MoEngage supports the following:
* Data tracking — [Track Events](/docs/developer-guide/android-sdk/data-tracking/basic/track-events) and [Track User Attributes](/docs/developer-guide/android-sdk/data-tracking/basic/track-user-attributes)
* [HTML In-App](/docs/developer-guide/android-sdk/in-app-messages/in-app-nativ)
* [Self-Handled In-App](/docs/developer-guide/android-sdk/in-app-messages/in-app-nativ#self-handled-inapps)
* [Self-handled Cards](/docs/developer-guide/android-sdk/cards/self-handled-cards)
# Cards
Source: https://moengage.com/docs/developer-guide/android-sdk/cards/cards
Set up MoEngage Cards to deliver persistent inbox and newsfeed messages to your Android app users.
Create targeted or automated App Inbox/NewsFeed messages that can be grouped into various categories, and target your users with different updates or offers that can stay in the Inbox/Feed over a designated period of time. For more information, refer to [Cards](https://www.moengage.com/docs/user-guide/campaigns-and-channels/cards/create/create-a-card-campaign).
# SDK Installation
## Install using BOM
Integration using BOM is the recommended way of integration; refer to the [Install Using BOM](https://www.moengage.com/docs/developer-guide/android-sdk/sdk-integration/basic-integration/Install-Using-BOM) document. Once you have configured the BOM add the dependency in the app/build.gradle file as shown below
```kotlin build.gradle.kts wrap theme={null}
dependencies {
implementation("com.moengage:cards-core")
}
```
Once the BOM is configured, include the specific MoEngage modules required for the application. \
Note: Version numbers are not required for these dependencies; the BOM automatically manages them.
### **SDK Dependency on Glide for Image and GIF Loading**
SDK uses [Glide](https://bumptech.github.io/glide/) for loading images and gifs. Ensure you add Glide to your application in case you don't have it already. SDK is compiled using library version **4.9.0.** We recommend you use the same version or higher in your application.
# Adding the UI Component
You can integrate the card UI into your application either by inflating the activity provided by the SDK or attaching the Fragment provided by the SDK to an existing Activity in the application.
## Integrating using Activity
The SDK manifest has declared the [*CardActivity*](https://moengage.github.io/android-api-reference/cards-ui/com.moengage.cards.ui/-card-activity/index.html) in the manifest file, and nothing additional is required until and unless you want to customize the theme of the Activity or any other launch property.\
The default declaration is as below.
```xml AndroidManifest.xml wrap theme={null}
```
For more information about how to customize, refer to Customisation.
## Integrating using Fragment
To integrate the Card UI as a fragment, you can inflate the [*CardFragment*](https://moengage.github.io/android-api-reference/cards-ui/com.moengage.cards.ui/-card-fragment/index.html) from the appropriate place inside your application.
# UI Customizations
SDK provides a certain set of UI customizations.
## Activity Customization
If you are integrating the activity provided by the SDK, you can customize the label, theme, etc.\
To customize the theme label you can either re-declare the activity in your app's manifest and provide the desired theme or label.\
Alternatively, you can override the SDK defaults as described below.
### Activity Label
The default label for the activity is **Inbox**. In case you want to change the label add the **moe\_card\_feed\_title** string in your **strings.xml** file with the label name.
```markdown strings.xml wrap theme={null}
[YOUR_LABEL_NAME]
```
### Activity Theme
The default theme applied to the Activity is **MoECardTheme.NoActionBar**.\
The following is the theme definition.
```xml colors.xml wrap theme={null}
```
```xml colors.xml wrap theme={null}
#1C64D0@color/moe_black@color/moe_white@color/moe_black
```
To customize the theme, override any of the color attributes.\
To override, define the attribute with the same name in the **colors.xml** file of the application and specify the desired color.\
For example, if you want to customize the accent color, define **moe\_card\_color\_accent** in your application's **color.xml** as described below and replace **\[YOUR\_COLOR]** with the desired color.
```xml colors.xml wrap theme={null}
[YOUR_COLOR]
```
### Activity Toolbar
The **CardActvity** has a toolbar in the layout with the style below. In case you want to customize any of the properties, override the style below completely, or you can override the individual items as well.
```xml styles.xml wrap theme={null}
```
The background color of the toolbar is set to the primary color of the theme.
## Tab Customisation
The Card UI is built with a tab layout, following is the list of customization options provided by the SDK.
* Text Appearance
* Font
* Text Size
* Tab text color selected/unselected
* Tab background-color selected/unselected
To customize the properties, you can override the styles or color attributes defined by the SDK in your application **colors.xml** or **styles.xml** file.
```xml style.xml wrap theme={null}
```
```xml colors.xml wrap theme={null}
#1C64D0#8E8E8E@color/moe_white@color/moe_white
```
## Text Customization
Each card has three text fields and a button
* Header
* Message
* Call to Action Button(CTA)
* Timestamp
The default style for each of these components can be overridden by overriding the below styles completely or overriding the individual items.\
Certain styling components like color and text formatting (like bold, italic, etc.) for these fields are customizable from the MoEngage Dashboard, while others, like font and text size fixed on the SDK side. Values for these can be overridden and will be applied to all cards.
The color defined in these styles are default colors and will be overridden by the colors selected during campaign creation.
```xml styles.xml wrap theme={null}
{/* Header style */}
{/* Message style */}
{/* Button style */}
{/* Timestamp Style */}
```
```xml colors.xml wrap theme={null}
{/* Text Color */}
#424242#616161#9E9E9E
```
### Timestamp format Customization
The default format for the timestamp shown on each card is **MMM dd, hh:mm a** this can be customized by passing the custom format to [*configureCards()*](https://moengage.github.io/android-api-reference/core/com.moengage.core/-mo-engage/-builder/configure-cards.html) of the [*MoEngage.Builder*](https://moengage.github.io/android-api-reference/core/com.moengage.core/-mo-engage/-builder/index.html)object while initializing the SDK.
## Card Customization
You can customize the following properties for a given card
* Un-clicked Indicator Color
* Card Background Color (overridden by the color selected during the campaign creation)
Override the below color resources in the **colors.xml** to use the desired color.
```xml colors.xml wrap theme={null}
#5956D6@color/moe_white
```
## Empty State
If the inbox does not contain any cards, SDK shows an empty screen with an image and message, as shown below.
The image and message can be customized if required.
#### Image Customization
To customize the image, pass in the resource id of the image in the [*configureCards()*](https://moengage.github.io/android-api-reference/core/com.moengage.core/-mo-engage/-builder/configure-cards.html) API [MoEngage.Builder](https://moengage.github.io/android-api-reference/core/com.moengage.core/-mo-engage/-builder/index.html) object while initializing the SDK.
#### Message Customisation
To customize the message, override the **moe\_card\_no\_message\_available** in your applications **strings.xml** file.
```xml strings.xml wrap theme={null}
[YOUR_STRING_GOES_HERE]
```
## Customize Delete Text
To customize the message of the delete button, override the below string resource in the **strings.xml** of your application.
```xml xml wrap theme={null}
[YOUR_STRING_GOES_HERE]
```
## Disable Pull to Refresh
By default, the Card Activity/Fragment has the pull to refresh feature enabled, you can disable pull to refresh using the [*configureCards()*](https://moengage.github.io/android-api-reference/core/com.moengage.core/-mo-engage/-builder/configure-cards.html)API in the [*MoEngage.Builder*](https://moengage.github.io/android-api-reference/core/com.moengage.core/-mo-engage/-builder/index.html)*.*
# APIs
## Unclicked Count
The SDK provides an API to fetch the number of cards which hasn't been clicked by the users. To get the count, you can use the below APIs.
```Kotlin Kotlin wrap theme={null}
// Call this API on worker thread as it reads from a file.
MoECardHelper.getUnClickedCardCount(context)
// This API returns the count asynchronously in the listener passed as a parameter.
MoECardHelper.getUnClickedCardCountAsync(context, listener)
```
```Java Java theme={null}
// Call this API on worker thread as it reads from a file.
MoECardHelper.INSTANCE.getUnClickedCardCount(context);
// This API returns the count asynchronously in the listener passed as a parameter.
MoECardHelper.INSTANCE.getUnClickedCardCountAsync(context, listener);
```
Refer to the documentation of [*MoECardHelper.getUnClickedCardCount()*](https://moengage.github.io/android-api-reference/cards-core/com.moengage.cards.core/-mo-e-card-helper/get-un-clicked-card-count.html) and [*MoECardHelper.getUnClickedCardCountAsync()*](https://moengage.github.io/android-api-reference/cards-core/com.moengage.cards.core/-mo-e-card-helper/get-un-clicked-card-count-async.html) for more information.
## New Card Count
The SDK provides an API to get the new cards for the user on the device. To get the count, use the below API.
```Kotlin Kotlin wrap theme={null}
// Call this API on worker thread as it reads from a file.
MoECardHelper.getNewCardCount(context)
// This API returns the count asynchronously in the listener passed as a parameter.
MoECardHelper.getNewCardCountAsync(context, listener)
```
```Java Java theme={null}
// Call this API on worker thread as it reads from a file.
MoECardHelper.INSTANCE.getNewCardCount(context);
// This API returns the count asynchronously in the listener passed as a parameter.
MoECardHelper.INSTANCE.getNewCardCountAsync(context, listener);
```
Refer to the documentation of [*MoECardHelper.getNewCardCount()*](https://moengage.github.io/android-api-reference/cards-core/com.moengage.cards.core/-mo-e-card-helper/get-new-card-count-async.html)and [*MoECardHelper.getNewCardCountAsync()*](https://moengage.github.io/android-api-reference/cards-core/com.moengage.cards.core/-mo-e-card-helper/get-new-card-count-async.html) for more information.
# Callbacks
The SDK provides callbacks when
* Cards are successfully synced on application launch/foreground
* The card is clicked by the user
## Sync Callback
To get a callback for sync completion on application launch/foreground implement the [*SyncCompleteListener*](https://moengage.github.io/android-api-reference/cards-core/com.moengage.cards.core.listener/-sync-complete-listener/index.html) interface and register for the callback using [*MoECardHelper.setSyncCompleteListener()*](https://moengage.github.io/android-api-reference/cards-core/com.moengage.cards.core/-mo-e-card-helper/set-sync-complete-listener.html)*.*
## Click Callback
To get a callback on the card, click implement the [*OnCardClickListener*](https://moengage.github.io/android-api-reference/cards-ui/com.moengage.cards.ui.listener/-on-card-click-listener/index.html) interface and register for the callback using the [*MoECardUiHelper.setClickListener().*](https://moengage.github.io/android-api-reference/cards-ui/com.moengage.cards.ui/-mo-e-card-ui-helper/set-click-listener.html)
# Self Handled Cards
Source: https://moengage.com/docs/developer-guide/android-sdk/cards/self-handled-cards
Build custom card views in your Android app using the MoEngage self-handled Cards SDK.
Self-handled cards give you the flexibility of creating Card Campaigns on the MoEngage Platform and displaying the cards anywhere inside the application. SDK provides APIs to fetch the campaign's data using which you can create your own view for cards.
# SDK Installation
## Installing using BOM
Integration using BOM is the recommended way of integration; refer to the [Install Using BOM](/docs/developer-guide/android-sdk/sdk-integration/basic-integration/Install-Using-BOM) document. Once you have configured the BOM add the dependency in the *app/build.gradle* file as shown below
```kotlin build.gradle.kts wrap theme={null}
dependencies {
...
implementation("com.moengage:cards-core")
}
```
Once the BOM is configured, include the specific MoEngage modules required for the application. \
Note: Version numbers are not required for these dependencies; the BOM automatically manages them.
# Implementing Self Handled Cards
Use the below APIs to fetch the card's data and build your own UI. The SDK provides both blocking and async APIs for fetching the data. In this document, we have just added the blocking APIs, refer to the API reference for [*MoECardHelper*](https://moengage.github.io/android-api-reference/cards-core/com.moengage.cards.core/-mo-e-card-helper/index.html) for the async APIs.
## Notify on Section Load/Unload
You can show the cards on a separate screen or a section of the screen. When the cards screen/section is loaded call [*onCardSectionLoaded()*](https://moengage.github.io/android-api-reference/cards-core/com.moengage.cards.core/-mo-e-card-helper/on-card-section-loaded.html) and call [*onCardSectionUnloaded()*](https://moengage.github.io/android-api-reference/cards-core/com.moengage.cards.core/-mo-e-card-helper/on-card-section-unloaded.html) when the screen/section is no longer visible or going to background.
```Kotlin Kotlin wrap theme={null}
// call on section or screen load
MoECardHelper.onCardSectionLoaded(context,listener);
// call when the section is no longer visible or going to background.
MoECardHelper.onCardSectionUnloaded(context);
```
```Java Java theme={null}
// call on section or screen load
MoECardHelper.INSTANCE.onCardSectionLoaded(context,listener);
// call when the section is no longer visible or going to background.
MoECardHelper.INSTANCE.onCardSectionUnloaded(context);
```
## Fetch Categories
To fetch all the categories for which cards are configured, use the [*getCardCategories()*](https://moengage.github.io/android-api-reference/cards-core/com.moengage.cards.core/-mo-e-card-helper/get-card-categories.html) API.
```Kotlin Kotlin wrap theme={null}
MoECardHelper.getCardCategories(context)
```
```Java Java theme={null}
MoECardHelper.INSTANCE.getCardCategories(context);
```
Additionally, you can optionally have an **All** category which would be like a superset of other categories. Use the [*isAllCategoryEnabled()*](https://moengage.github.io/android-api-reference/cards-core/com.moengage.cards.core/-mo-e-card-helper/is-all-category-enabled.html) API.
```Kotlin Kotlin wrap theme={null}
MoECardHelper.isAllCategoryEnabled(context)
```
```Java Java theme={null}
MoECardHelper.INSTANCE.isAllCategoryEnabled(context);
```
## Fetch Cards for Categories
To fetch cards eligible for display for a specific category, use the [*getCardsForCategory()*](https://moengage.github.io/android-api-reference/cards-core/com.moengage.cards.core/-mo-e-card-helper/get-cards-for-category.html) API
```Kotlin Kotlin wrap theme={null}
MoECardHelper.getCardsForCategory(context, "[YOUR_CATEGORY]")
```
```Java Java theme={null}
MoECardHelper.INSTANCE.getCardsForCategory(context, "[YOUR_CATEGORY]");
```
To fetch all the cards eligible for display irrespective of the category, pass the category **CARD\_CATEGORY\_ALL** as shown below
```Kotlin Kotlin wrap theme={null}
MoECardHelper.getCardsForCategory(context, CARDS_CATEGORY_ALL)
```
```Java Java theme={null}
MoECardHelper.INSTANCE.getCardsForCategory(context, MoECardsCoreConstants.CARDS_CATEGORY_ALL);
```
Refer to the documentation of the [*Card*](https://moengage.github.io/android-api-reference/cards-core/com.moengage.cards.core.model/-card/index.html) model to know more about the fields and data present.
Instead of using separate APIs to fetch the Cards and categories, you can use the [*getCardsInfo()*](https://moengage.github.io/android-api-reference/cards-core/com.moengage.cards.core/-mo-e-card-helper/get-cards-info.html) API to fetch all the information in one go
```Kotlin Kotlin wrap theme={null}
MoECardHelper.getCardsInfo(context)
```
```Java Java theme={null}
MoECardHelper.INSTANCE.getCardsInfo(context);
```
## Widget and Widget Id Mapping
### Basic Card/Illustration Card
| Widget Id | Widget Type | Widget Information |
| --------- | -------------------------- | --------------------------------- |
| 0 | Image (WidgetType.IMAGE) | Image widget in the card. |
| 1 | Text (WidgetType.TEXT) | Header text for the card. |
| 2 | Text (WidgetType.TEXT) | Message text for the card. |
| 3 | Button (WidgetType.Button) | Call to action(CTA) for the card. |
## Track Statistics for Cards
Since the UI/display of the cards is controlled by the application to track statistics on delivery, display, and click, we need the application to notify the SDK.
### Delivered
To track delivery to the card section of the application, call the [*cardDelivered()*](https://moengage.github.io/android-api-reference/cards-core/com.moengage.cards.core/-mo-e-card-helper/card-delivered.html) API when the cards section of the application is loaded.
```Kotlin Kotlin wrap theme={null}
MoECardHelper.cardDelivered(context)
```
```Java Java theme={null}
MoECardHelper.INSTANCE.cardDelivered(context);
```
### Impression
Call the [*cardShown()*](https://moengage.github.io/android-api-reference/cards-core/com.moengage.cards.core/-mo-e-card-helper/card-shown.html) API when a specific card is visible on the screen.
```Kotlin Kotlin wrap theme={null}
MoECardHelper.cardShown(context, card)
```
```Java Java theme={null}
MoECardHelper.INSTANCE.cardShown(context, card);
```
### Click
Call the [*cardClicked()*](https://moengage.github.io/android-api-reference/cards-core/com.moengage.cards.core/-mo-e-card-helper/card-clicked.html) API whenever a user clicks on a card, along with the card object widget identifier for the UI element clicked should also be passed.
```Kotlin Kotlin wrap theme={null}
MoECardHelper.cardClicked(context, card, widgetId)
```
```Java Java theme={null}
MoECardHelper.INSTANCE.cardClicked(context, card, widgetId);
```
## Delete Card
Call the [*deleteCard()*](https://moengage.github.io/android-api-reference/cards-core/com.moengage.cards.core/-mo-e-card-helper/delete-card.html) API to delete a card
```Kotlin Kotlin wrap theme={null}
MoECardHelper.deleteCard(context, card)
```
```Java Java theme={null}
MoECardHelper.INSTANCE.deleteCard(context, card);
```
To delete a list of cards, use [*deleteCards()*](https://moengage.github.io/android-api-reference/cards-core/com.moengage.cards.core/-mo-e-card-helper/delete-cards.html) API.
## Refresh Cards from the Server
Use the [*fetchCards()*](https://moengage.github.io/android-api-reference/cards-core/com.moengage.cards.core/-mo-e-card-helper/fetch-cards.html) API to refresh cards from the MoEngage server if required.
```Kotlin Kotlin wrap theme={null}
MoECardHelper.fetchCards(Context, CardAvailableListener)
```
```Java Java theme={null}
MoECardHelper.INSTANCE.fetchCards(Context, CardAvailableListener);
```
* The SDK automatically refreshes/fetches cards from the MoEngage server whenever the application comes to the foreground.
* This API has a FUP if breached the existing cards i.e. the ones in the local storage of the device will be passed on in the callback.
* For details on the sync timing and rate limits for `fetchCards()`, see [When Does the MoEngage SDK Sync Card Data?](/docs/user-guide/campaigns-and-channels/cards/faqs-cards/when-does-the-moengage-sdk-sync-card-data)
Please take a look at the [documentation](https://moengage.github.io/android-api-reference/cards-core/com.moengage.cards.core/-mo-e-card-helper/index.html) for a complete guide on available helper APIs.
# Release Checklist
Source: https://moengage.com/docs/developer-guide/android-sdk/checklist/release-checklist
Verify your MoEngage Android SDK integration against this checklist before releasing your app.
Before releasing your application with MoEngage SDK integrated verify if the following items have been implemented.
# General
* SDK is initialized in *onCreate()* of the Application class on the Main Thread.
* Disable logging for release build if enabled.
* Enable Java 8 target and source compatibility.
# Data Tracking
* Install/Update Differentiation
* Exhaustive Events and Attributes are tracked based on use-cases.
* User attributes tracked for both new and existing users
* Track the unique identifier for the user on login using `identifyUser()`. `setUniqueId()` is deprecated since SDK 13.6.00 — see [Track User Attributes](/docs/developer-guide/android-sdk/data-tracking/basic/track-user-attributes#identifying-users).
* The logout method is called when the user is logged out of the application
# Push Notification
## FCM
* Firebase messaging dependency added and related Firebase plugins added.
* Small icon and large icon set along with other optional metadata
* In case you are using vector drawables, .webp or any file format other than .png images for notification icons, please make sure you have tested on all OS versions supported by your app.
* Check Firebase receiver in the **AndoidManifest.xml** file. Make sure only 1 receiver is added in the manifest file.
* Test Push on a physical device
* Token Registration and Payload handled by App
* Disable token registration of MoEngage SDK
* Token passed to the SDK for a fresh install, app update, and whenever FCM refreshes the token.
* Push Payload passed on to the SDK
* Token Registration and Payload handled by SDK
* SDK's FCM receiver added in the manifest
* Token change listener registered if required.
## Push Amp Plus
### Push Kit
* Push Kit dependency added and related HMS plugins configured.
* Check version compatibility of PushKit dependency with **moe-android-sdk**
* Test on Huawei Device
## Push Templates
* Check version compatibility of the Push Templates dependency with **moe-android-sdk**
* Test a template campaign on the device.
# In-App
* Glide dependency added — required for image and GIF rendering starting in-app version 7.0.0.
* Call `showInApp()` on all Activity or Fragment where you want to show in-apps.
* Call `showNudge()` on the Activity or Fragment where you want to show nudges.
* Test in-app campaigns on a device.
# Inbox
* Check version compatibility of Inbox dependency with **moe-android-sdk**
* Check if campaigns are visible in the applications inbox.
# Cards
* Check version compatibility of the Cards dependency with **moe-android-sdk**
* Check if card campaigns are visible on the device.
# Geofence
* Check version compatibility of the Geofence dependency with **moe-android-sdk**
* Application has added Google’s Location Services as a dependency in the application.
* Application requests appropriate permissions based on the OS version.
# Compliance
Source: https://moengage.com/docs/developer-guide/android-sdk/compliance/compliance
Enable or disable data tracking and manage user consent in the MoEngage Android SDK.
# Enable/Disable Data Tracking
To stop the MoEngage SDK from tracking custom events or user attributes, use the [disableDataTracking()](https://moengage.github.io/android-api-reference/core/com.moengage.core/disable-data-tracking.html) API. The SDK continues to collect analytical data even when data tracking is disabled.
```kotlin Kotlin wrap theme={null}
import com.moengage.core.disableDataTracking
disableDataTracking(context)
```
```java Java theme={null}
MoESdkStateHelper.disableDataTracking(context);
```
Once the above API is called, no custom events or user attributes will be tracked. SDK will reject all events until [enableDataTracking()](https://moengage.github.io/android-api-reference/core/com.moengage.core/enable-data-tracking.html) is called.\
Once you want to track events or user attributes, call the below API.
```kotlin Kotlin wrap theme={null}
import com.moengage.core.enableDataTracking
enableDataTracking(context)
```
```java Java theme={null}
MoESdkStateHelper.enableDataTracking(context);
```
# Enable/Disable SDK
To stop the MoEngage SDK from tracking any user information or sending any data to the MoEngage system, use the [disableSdk()](https://moengage.github.io/android-api-reference/core/com.moengage.core/disable-sdk.html) API.
```kotlin Kotlin wrap theme={null}
import com.moengage.core.disableSdk
disableSdk(context)
```
```java Java theme={null}
MoESdkStateHelper.disableSdk(context);
```
Once this API is called, all the SDK APIs will be non-operational. SDK will be disabled until [enableSdk()](https://moengage.github.io/android-api-reference/core/com.moengage.core/enable-sdk.html) is called.\
Once you have the user's consent use the below API to enable the SDK.
```kotlin Kotlin wrap theme={null}
import com.moengage.core.enableSdk
enableSdk(context)
```
```java Java theme={null}
MoESdkStateHelper.enableSdk(context);
```
# Delete User Data
In April 2023, Google Announced the [User Account Deletion Policy](https://android-developers.googleblog.com/2023/04/giving-people-more-control-over-their-data.html), according to which if your app allows you to create an account within your app, it must also allow the user to delete their account within the app.
Calling this API will delete the User Data/Profile from the MoEngage Server. You need to have a minimum SDK version of **12.10.00** or **above**to call this API.
```kotlin Kotlin wrap theme={null}
import com.moengage.core.MoECoreHelper
MoECoreHelper.deleteUser(context,listener)
```
```java Java theme={null}
import com.moengage.core.MoECoreHelper;
MoECoreHelper.INSTANCE.deleteUser(context,listener);
```
For more information, refer to the [API Documentation.](https://moengage.github.io/android-api-reference/core/com.moengage.core/-mo-e-core-helper/delete-user.html)
# Prepare for Google Play's data disclosure requirements
Source: https://moengage.com/docs/developer-guide/android-sdk/compliance/prepare-for-google-plays-data-disclosure-requirements
Review MoEngage Android SDK data collection details to complete Google Play's Data Safety disclosure.
In May 2021, Google Play [announced the new Data safety section](https://android-developers.googleblog.com/2021/05/new-safety-section-in-google-play-will.html), which is a developer-provided disclosure for an app's data collection, sharing, and security practices.
This page can help you complete the requirements for this data disclosure in regards to your usage of the MoEngage Android SDK. On this page, you can find information on whether and how our SDK handles end-user data, including any applicable settings or configurations you can control as the application developer.
We aim to be as transparent as possible in supporting you; however, as the application developer, you are solely responsible for deciding how to respond to Google Play's Data safety section form regarding your app's end-user data collection, sharing, and security practices.
# How to use the information on this page?
This page lists the end-user data collected only from *version **12.2.01*** of the MoEngage Android SDK.
If you are using a prior version of the MoEngage Android SDK, could you update to the latest version to ensure your app's disclosures are accurate? The MoEngage Android SDK will continue to be updated over time. This article will reflect these changes, so update your disclosures as necessary.
# Data collected
## Mandatory Data Collected
SDK collects the following data automatically for analytics.
| Data | By default, MoEngage SDK ... |
| ------------------------- | ---------------------------------------------------------------------------------------------------------------------------- |
| IP Address | Collects the device's IP address, which may be used to estimate the general location of a device. |
| User product interactions | Collects user-product interactions and interaction information, including app launch, and application foreground-background. |
| Device Metadata | Collects device information like Device Model, OS version, Timezone, network type(if permission is granted by the user), |
## Optional Data Collected
SDK optionally collects the below data based on whether the application/user has given consent for tracking the data.
| Data | MoEngage SDK... |
| ------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------- |
| Device Metadata | Collects device information like Product Name, Manufacturer, Device Dimensions, Display type, and Carrier(if permission is granted by the user). |
| Device Identifiers | Collects device identifiers like Advertising Identifier, and Android Id. |
# Configuring Opt-outs
Source: https://moengage.com/docs/developer-guide/android-sdk/data-tracking/advanced-or-optional/configuring-opt-outs
Configure tracking opt-outs for device identifiers like GAID and Android ID in the MoEngage Android SDK.
By default, SDK tracks certain device identifiers like GAID, Android-id, activity names, etc. If required you can choose to opt-out of this tracking by using the TrackingOptOutConfig.\
Refer to the API reference of [TrackingOptOutConfig](https://moengage.github.io/android-api-reference/core/com.moengage.core.config/-tracking-opt-out-config/index.html) for more details on the available opt-outs. Use the [configureTrackingOptOut()](https://moengage.github.io/android-api-reference/core/com.moengage.core/-mo-engage/-builder/configure-tracking-opt-out.html) to pass on the configuration to the SDK.
```Kotlin Kotlin wrap theme={null}
val trackingOptOut = mutableSetOf>()
trackingOptOut.add(YourActivityName::class.java)
val trackingOptOutConfig = TrackingOptOutConfig(
isCarrierTrackingEnabled = true,
isDeviceAttributeTrackingEnabled = true,
trackingOptOut
)
val moengage = MoEngage.Builder(
application = application,
appId = appId,
dataCenter = DataCenter.DATA_CENTER_X
)
.configureTrackingOptOut(trackingOptOutConfig)
.build()
MoEngage.initialiseDefaultInstance(moengage)
```
```Java Java theme={null}
Set> trackingOptOut = new HashSet<>();
trackingOptOut.add(YourActivityName.class);
MoEngage moEngage = new MoEngage.Builder(application, appId, DataCenter.DATA_CENTER_X)
.configureTrackingOptOut(new TrackingOptOutConfig(true, true, trackingOptOut))
.build();
MoEngage.initialiseDefaultInstance(moEngage);
```
# Device Identifier Tracking
Source: https://moengage.com/docs/developer-guide/android-sdk/data-tracking/advanced-or-optional/device-identifier-tracking
Enable or disable Android ID and advertising identifier tracking in the MoEngage Android SDK.
## Android Id Tracking
From SDK version **11.5.00**, SDK optionally tracks Android-id for the devices(by default Android-id is not tracked). To enable Android-id tracking use the [enableAndroidIdTracking()](https://moengage.github.io/android-api-reference/core/com.moengage.core/enable-android-id-tracking.html) method as shown below.
```Kotlin Kotlin wrap theme={null}
import com.moengage.core.enableAndroidIdTracking
enableAndroidIdTracking(context)
```
```Java Java theme={null}
MoESdkStateHelper.enableAndroidIdTracking(context);
```
Before enabling the Android-id tracking application should take consent from the user as per the [Google Policy](https://developer.android.com/identity/user-data-ids).Once tracking is enabled SDK would continue tracking the Android-id until explicitly opted-out. Use the [disableAndroidIdTracking()](https://moengage.github.io/android-api-reference/core/com.moengage.core/disable-android-id-tracking.html) method to opt-out of Android-id tracking.
```Kotlin Kotlin wrap theme={null}
import com.moengage.core.disableAndroidIdTracking
disableAndroidIdTracking(context)
```
```Java Java theme={null}
MoESdkStateHelper.disableAndroidIdTracking(context);
```
For the SDK version below **11.5.00** use the *TrackingOptoutConfig* while initializing the MoEngage SDK.
## Advertising Identifier Tracking
Before you enable advertising ID tracking, make sure you've added the `play-services-ads-identifier` dependency described in [Enable Advertising Identifier Tracking](/docs/developer-guide/android-sdk/data-tracking/basic/enable-advertising-identifier-tracking).
From SDK version **12.1.00**, SDK optionally tracks Advertising Identifier for the devices(by default Advertising Identifier is not tracked). To enable Advertising Identifier tracking use the [enableAdIdTracking()](https://moengage.github.io/android-api-reference/core/com.moengage.core/enable-ad-id-tracking.html) method as shown below.
```Kotlin Kotlin wrap theme={null}
import com.moengage.core.enableAdIdTracking
enableAdIdTracking(context)
```
```Java Java theme={null}
MoESdkStateHelper.enableAdIdTracking(context);
```
Before enabling the Advertising Identifier tracking application should take consent from the user as per the [Google Policy](https://support.google.com/googleplay/android-developer/answer/10144311).
Once tracking is enabled SDK would continue tracking the Advertising Identifier until explicitly opted-out. Use the [disableAdIdTracking()](https://moengage.github.io/android-api-reference/core/com.moengage.core/disable-ad-id-tracking.html) method to opt-out of Advertising Identifier tracking.
```Kotlin Kotlin wrap theme={null}
import com.moengage.core.disableAdIdTracking
disableAdIdTracking(context)
```
```Java Java theme={null}
MoESdkStateHelper.disableAdIdTracking(context);
```
For the SDK version below **12.1.00** use the *TrackingOptoutConfig* while initializing the MoEngage SDK.
# Personalize Experience Events Tracking
Source: https://moengage.com/docs/developer-guide/android-sdk/data-tracking/advanced-or-optional/personalize-experience-events-tracking
Track impression and click events for personalized experiences from the MoEngage Personalize API on Android.
This document covers the helper APIs for reporting impressions and clicks when you fetch personalized experiences through the [MoEngage Personalize REST API](https://www.moengage.com/docs/api/experiences/fetch-experience). Use this page if your app calls the Personalize API directly from your server.
If you are integrating MoEngage Personalize natively from your Android app, use the [Personalize SDK](/docs/developer-guide/android-sdk/personalize/personalize-sdk) instead. The Personalize SDK fetches experiences and tracks impressions and clicks through a single helper, so you don't need the APIs on this page.
# Prerequisites
## SDK version
> You must update your Native Android SDK catalog version to **5.2.0** or higher.
## MoEngage Account Configuration
Ensure your MoEngage workspace is enabled to utilize the Personalize API. Refer to [this article](https://www.moengage.com/docs/user-guide/personalize/server-side-personalization/create-server-side-personalization-experience) for details on setting up Personalize API experiences.
# Reporting Experience Shown events
The SDK provides helper APIs to track shown events; please refer to the [API documentation](https://moengage.github.io/android-api-reference/personalization-core/com.moengage.campaigns.personalize/-mo-e-personalize-helper/experience-shown.html) for more details.
Impressions should be reported when an experience is visually presented to the user.
## Single Experience
To report an impression for a single experience, pass the **experience\_context** of the experience as a map.
**experience\_context** is a JSON object that is returned as part of the [response of the Personalize API request](https://www.moengage.com/docs/api/experiences/fetch-experience).
```Kotlin Kotlin wrap theme={null}
MoEPersonalizeHelper.experienceShown(context, experienceContextMap)
```
```Java Java wrap theme={null}
MoEPersonalizeHelper.INSTANCE.experienceShown(context, experienceContextMap);
```
## Multiple Experiences
To track the experience shown event for multiple experiences, pass the **list** of **experience\_context** of each experience as a map.
```Kotlin Kotlin wrap theme={null}
MoEPersonalizeHelper.experienceShown(context, experienceContextMapList)
```
```Java Java wrap theme={null}
MoEPersonalizeHelper.INSTANCE.experienceShown(context, experienceContextMapList);
```
# Reporting Experience Clicked events
The SDK provides helper APIs to track clicked events; refer to the [API documentation](https://moengage.github.io/android-api-reference/personalization-core/com.moengage.campaigns.personalize/-mo-e-personalize-helper/experience-clicked.html) for more details.
Clicked events should be reported when a user clicks on any element that has been personalized using the response of the Personalize API.
## Single Experience
To report a click event for a single experience, pass the **experience\_context** of the experience as a map.
```Kotlin Kotlin wrap theme={null}
MoEPersonalizeHelper.experienceClicked(context, experienceContextMap)
```
```Java Java wrap theme={null}
MoEPersonalizeHelper.INSTANCE.experienceClicked(context, experienceContextMap);
```
## Multiple Experiences
To track the experience shown event for multiple experiences, pass the **list** of **experience\_context** of each experience as a map.
```Kotlin Kotlin wrap theme={null}
MoEPersonalizeHelper.experienceClicked(context, experienceContextMapList)
```
```Java Java wrap theme={null}
MoEPersonalizeHelper.INSTANCE.experienceClicked(context, experienceContextMapList);
```
You can optionally include a **b\_id** key in the **experience\_context** object to provide additional context about the click. Its value should describe the specific component or interaction within the experience that was clicked. This is particularly useful for experiences composed of multiple interactive elements, helping to distinguish between clicks on different parts of the same overall experience.
# Tracking Locale
Source: https://moengage.com/docs/developer-guide/android-sdk/data-tracking/advanced-or-optional/tracking-locale
Optionally track the device locale in your Android app using the MoEngage SDK.
**Optional**
This is optional and only required if you wish to track the locale.
SDK by default does not track locales. To track the Locale set for the given JVM instance call [MoEAnalyticsHelper.trackDeviceLocale()](https://moengage.github.io/android-api-reference/core/com.moengage.core.analytics/-mo-e-analytics-helper/track-device-locale.html).
```Kotlin Kotlin wrap theme={null}
MoEAnalyticsHelper.trackDeviceLocale(context)
```
```Java Java wrap theme={null}
MoEAnalyticsHelper.INSTANCE.trackDeviceLocale(context);
```
# Enable Advertising Identifier Tracking
Source: https://moengage.com/docs/developer-guide/android-sdk/data-tracking/basic/enable-advertising-identifier-tracking
Enable advertising identifier tracking in the MoEngage Android SDK for accurate device analytics.
For accurate analytics around devices and tracking Re-installs, it is essential to track the Advertising Identifier. For more information, refer to [Android Advertising ID Tracking](https://www.moengage.com/docs/user-guide/data/privacy/android-advertising-id-tracking).
## Add Ad Identifier Library
Add the below dependency in the application-level ***build.gradle*** file.
```groovy Groovy wrap theme={null}
implementation("com.google.android.gms:play-services-ads-identifier:18.0.1")
```
To enable Advertising Identifier tracking use the [enableAdIdTracking()](https://moengage.github.io/android-api-reference/core/com.moengage.core/enable-ad-id-tracking.html) method as shown below.
```Kotlin Kotlin wrap theme={null}
import com.moengage.core.enableAdIdTracking
enableAdIdTracking(context)
```
```Java Java wrap theme={null}
MoESdkStateHelper.enableAdIdTracking(context);
```
Please ensure the application complies with the [Google Play policy](https://play.google/developer-content-policy/) regarding advertising ID tracking.
To disable advertising identifier tracking, or to learn how to track Android ID alongside the advertising ID, see [Device Identifier Tracking](/docs/developer-guide/android-sdk/data-tracking/advanced-or-optional/device-identifier-tracking).
# Setting Unique Id for SDK versions below 13.6.00
Source: https://moengage.com/docs/developer-guide/android-sdk/data-tracking/basic/setting-unique-id-for-sdk-versions-below-13-6-00
Set a unique user ID for login and logout in MoEngage Android SDK versions below 13.6.00.
# Implementing Login/Logout
* It's important to set the User Attribute Unique ID when a user logs into your app.
* This merges the new user with the existing user, if any exists, and will help prevent the creation of unnecessary/stale users.
* Setting the Unique ID is a critical piece to tie a user across devices and installs/uninstalls as well across all platforms (i.e. iOS, Android, Windows, The Web). Set the **USER\_ATTRIBUTE\_UNIQUE\_ID** attribute as soon as the user is **logged in**. Unique ID can be something like an email ID, a username (unique), or a database ID or any Backend generated ID.
* Do not set this for the user who not logged in.
## Login
```kotlin Kotlin wrap theme={null}
MoEAnalyticsHelper.setUniqueId(context, UNIQUE_ID)
```
```java Java theme={null}
MoEAnalyticsHelper.INSTANCE.setUniqueId(context, UNIQUE_ID);
```
**Note:** The following values are not allowed in the UniqueID field: "unknown", "guest", "null", "0", "1", "true", "false", "user\_attribute\_unique\_id", "(empty)", "na", "n/a", "", "dummy\_seller\_code", "user\_id", "id", "customer\_id", "uid", "userid", "none", "-2", "-1", "2"
**UNIQUE ID chaos**
* When you go live with MoEngage Android SDK for the first time, please ensure that you are setting the Unique ID of the already logged-in user along with other user attributes.
* Kindly make sure that you are not using a single UNIQUE ID for all the users, this can happen if you hardcode the value, instead of fetching it from your servers.
* If you pass 2 different UNIQUE ID information without calling the [logoutUser](https://moengage.github.io/android-api-reference/core/com.moengage.core/-mo-e-core-helper/logout-user.html) method in between, the SDK will internally force the logout of the existing user.
## Logout
The application needs to notify the MoEngage SDK whenever the user is logged out of the application. To notify the SDK, call the API whenever the user is logged out of the application.
```Kotlin Kotlin wrap theme={null}
MoECoreHelper.logoutUser(context)
```
```Java Java theme={null}
MoECoreHelper.INSTANCE.logoutUser(context);
```
If the application is registering for a push token, it should pass the new push token to MoEngage SDK after the user logs out. For more information about passing push tokens, refer to [Push Configuration for Android SDK](https://www.moengage.com/docs/developer-guide/android-sdk/push/basic/push-configuration).
## Updating User Attribute Unique Id
Use the method *setAlias()* to update the user attribute unique id instead of *setUniqueId()* with a different value. Using the method *setUniqueId()* with a new value creates unintended users in MoEngage.
```Kotlin Kotlin wrap theme={null}
MoEAnalyticsHelper.setAlias(ALIAS)
```
```Java Java theme={null}
MoEAnalyticsHelper.INSTANCE.setAlias(ALIAS);
```
# Track Events
Source: https://moengage.com/docs/developer-guide/android-sdk/data-tracking/basic/track-events
Track custom user events and their attributes in your Android app using the MoEngage SDK.
SDK adheres to the MoEngage FUP policies. For more information, refer to the [Fair Usage Policy](https://www.moengage.com/docs/user-guide/data/key-concepts/fair-usage-policy-fup#fair-usage-policy-fup).
Tracking events is how you record any actions your users perform, along with any properties that describe the action. Every trackEvent call records a single user action. We recommend that you make your event names human-readable so that everyone on your team can know what they mean instantly.
You can track an event using trackEvent with the event name and its characteristics (attributes/properties).
Every event has 2 attributes, action name, and key, value pairs which represent additional information about the action. Add all the additional information which you think would be useful for segmentation while creating campaigns. For eg., the following code tracks a purchase event of a product. We are including attributes like amount, quantity, a category that describes the event we are tracking.
For eg. The following code tracks an `Purchase` event. We are including attributes such as the `quantity`, `product name` that describes the event we are tracking.
```Kotlin Kotlin wrap theme={null}
val properties = Properties()
properties
// tracking integer
.addAttribute("quantity", 2)
// tracking string
.addAttribute("product", "iPhone")
// tracking date
.addAttribute("purchaseDate", Date())
// tracking double
.addAttribute("price", 5999.99)
// tracking location
.addAttribute("userLocation", GeoLocation(40.77, 73.98))
// tracking JSONArray
.addAttribute("jsonArrayAttr", JSONArray(listOf(1, 2, 3)))
// tracking JSONObject
.addAttribute("jsonObjectAttr", JSONObject().put("name", "value"))
MoEAnalyticsHelper.trackEvent(context, "Purchase", properties)
```
```Java Java wrap theme={null}
Properties properties = new Properties();
properties
// tracking integer
.addAttribute("quantity", 2)
// tracking string
.addAttribute("product", "iPhone")
// tracking Date
.addAttribute("purchaseDate", new Date())
// tracking double
.addAttribute("price", 5999.99)
// tracking location
.addAttribute("userLocation", new GeoLocation(40.77, 73.98))
// tracking JSONObject
.addAttribute("jsonObjectAttr", new JSONObject())
// tracking JSONArray
.addAttribute("jsonArrayAttr", new JSONArray());
MoEAnalyticsHelper.INSTANCE.trackEvent(context, "Purchase", properties);
```
context - context instance, change the name accordingly.
# Analytics
MoEngage SDK version 9.7.01 and later tracks user session and application traffic source.
User session tracking provides the flexibility to selectively mark events as non-interactive.
## Non-interactive event
Events that do not affect the session duration calculation in MoEngage Analytics are marked as Non-Interactive events.
The following are considered non-interactive events:
* Do not start a new session, even when the app is in the foreground
* Do not extend the session
* Do not have information on source and session
An event is marked as non-interactive using the setNonInteractive() in the PayloadBuilder provided by the SDK to build event attributes.
For example,
```Kotlin Kotlin wrap theme={null}
val properties = Properties()
properties.addAttribute("quantity", 2)
.addAttribute("product", "iPhone")
.addAttribute("purchaseDate", Date())
.addAttribute("price", 5999.99)
.addAttribute("currency", "dollar")
.addAttribute("jsonObjectAttr", JSONObject().put("name", "value"))
.addAttribute("jsonArrayAttr", JSONArray(listOf(1, 2, 3)))
.setNonInteractive()
MoEAnalyticsHelper.trackEvent(context, "Purchase", properties)
```
```Java Java wrap theme={null}
Properties properties = new Properties();
properties.addAttribute("quantity", 2)
.addAttribute("product", "iPhone")
.addAttribute("purchaseDate", new Date())
.addAttribute("price", 5999.99)
.addAttribute("currency", "dollar")
.addAttribute("jsonObjectAttr", new JSONObject())
.addAttribute("jsonArrayAttr", new JSONArray())
.setNonInteractive();
MoEAnalyticsHelper.INSTANCE.trackEvent(context, "Purchase", properties);
```
For more information, refer to [MoEAnalyticsHelper#trackEvent](https://moengage.github.io/android-api-reference/core/com.moengage.core.analytics/-mo-e-analytics-helper/track-event.html) and [Properties](https://moengage.github.io/android-api-reference/core/com.moengage.core/-properties/index.html).
## Validations and restrictions
Event attributes have two layers of validation that apply across both debug and release builds:
* **Naming and format rules** — these always apply, regardless of build configuration.
* **Type validation** — invalid attribute values cause a fatal exception in debug builds and are silently dropped from the payload in release builds. The rest of the event is tracked.
### Naming and format rules
* **Reserved prefixes.** You cannot use `moe_` as a prefix when naming events, event attributes, or user attributes. It is a system prefix, and using it might result in periodic blocklisting without prior communication.
### Supported attribute value types
MoEngage supports the following data types: `String`, `Integer`, `Long`, `Double`, `Float`, `Boolean`, `Date`, `GeoLocation`, `JSONObject`, and `JSONArray`. If an unsupported value is passed:
* Starting from SDK version **13.6.00**, in debug builds the SDK throws an exception and crashes the app to surface data issues early in development.
* In release builds, the SDK silently drops the specific invalid attribute and logs the issue. The rest of the event payload is still tracked.
# Track Custom Event for Exit Intent
MoEngage SDK optionally notifies the application whenever the goes to the background. The application can track the custom event in this callback for exit intent. To get notified implement the [AppBackgroundListener](https://moengage.github.io/android-api-reference/core/com.moengage.core.listeners/-app-background-listener/index.html). Register the listener in the *onCreate()* of your Application class using *MoECallbacks.getInstance().addAppBackgroundListener()*.
You can not use "moe\_" as a prefix while naming events, event attributes, or user attributes. It is a system prefix and using it might result in periodic blacklisting without prior communication.
# Track Install or Update
Source: https://moengage.com/docs/developer-guide/android-sdk/data-tracking/basic/track-install-or-update
Differentiate between app installs and updates in the MoEngage Android SDK using the setAppStatus API.
Call `setAppStatus()` so the SDK knows whether the current launch is a fresh install or an update to an existing install. The SDK doesn't infer this on its own — your app must determine the state and pass the correct enum value.
```kotlin Kotlin wrap theme={null}
// Fresh install of the app
MoEAnalyticsHelper.setAppStatus(context, AppStatus.INSTALL)
// Existing user who updated the app
MoEAnalyticsHelper.setAppStatus(context, AppStatus.UPDATE)
```
```java Java theme={null}
// Fresh install of the app
MoEAnalyticsHelper.INSTANCE.setAppStatus(context, AppStatus.INSTALL);
// Existing user who updated the app
MoEAnalyticsHelper.INSTANCE.setAppStatus(context, AppStatus.UPDATE);
```
If you uninstall and reinstall your app, the SDK considers it a fresh install.
# Track User Attributes
Source: https://moengage.com/docs/developer-guide/android-sdk/data-tracking/basic/track-user-attributes
Track user attributes and set unique identifiers for user identification in the MoEngage Android SDK.
User Attributes are pieces of information you know about a user. They could be demographics like age or gender, account-specific like plan, or whether a user has seen a particular A/B test variation. User attributes are a customer identity you can reference throughout the customer’s lifetime.
# Identifying Users
For SDK versions below 13.6.00 refer to [this document](https://www.moengage.com/docs/developer-guide/android-sdk/data-tracking/basic/setting-unique-id-for-sdk-versions-below-13-6-00).
Setting identifiers is important to:
* To tie user behavior across platforms, i.e., iOS, Android, Web, etc.
* This is to ensure unnecessary or stale users are not created.
* To identify users across installs/re-installs.
## Single Identifier
Call the API below to pass the identifier on to the MoEngage SDK.
```kotlin Kotlin wrap theme={null}
MoEAnalyticsHelper.identifyUser(context, "identifier")
```
```java Java theme={null}
MoEAnalyticsHelper.INSTANCE.identifyUser(context, "identifier");
```
*Note*: This method is a replacement for the deprecated ***setUniqueId()***. If you are using ***setUniqueId()*** in your application, consider replacing it with ***identifyUser()***
## Multiple Identifiers
If your application has multiple identifiers using which you identify a user you can pass all the identifiers to the SDK using the below API
```kotlin Kotlin wrap theme={null}
MoEAnalyticsHelper.identifyUser(
context,
mapOf("identifierName1" to "identifierValue1", "identifierName2" to "identifierValue2")
)
```
```java Java theme={null}
Map identifiers = new HashMap() {{
put("identifierName1", "identifierValue1");
put("identifierName2", "identifierValue2");
}};
MoEAnalyticsHelper.INSTANCE.identifyUser(context, identifiers);
```
**Updates to SDK functions for User Identification and Session Management**
* **Forced Logout:** The MoEngage SDK no longer automatically logs out the previous user when a new user is detected on the device. Logout should now be explicitly called for workspaces enabled with Identity resolution to avoid data corruption.
* **SetUniqueID:** `identifyUser` function supports multiple identifiers, which replaces the need of using `SetUniqueID` function for user identification. Note that `SetUniqueID` is marked for removal in the future releases of SDK versions - it is important to use `identifyUser` instead especially if you are using Identity resolution in your workspace.
* **SetAlias:** For workspaces with the Identity resolution feature enabled, MoEngage SDK stores the previous identifier values. When `identifyUser` function is used to track the new identifier values, MoEngage SDK detects the change in identifier value and reports accordingly.
If you call the `identifyUser` function without logging out, then the existing logged-in user's ID is updated.
If you call `identifyUser()` multiple times with different identifier names, the SDK will append this identifier to the already set identifiers.
Refer to our help [document](/docs/user-guide/data/user-data/unified-identity-identity-resolution) to learn more about the feature.
If a network issue prevents the identifier from reaching the MoEngage server, the SDK caches the `identifyUser()` call and retries automatically once the network is available or the next time the app opens.
For more information, refer to:
* [Enable/Disable SDK](/docs/developer-guide/android-sdk/compliance/compliance#enabledisable-sdk)
* [Enable/Disable Data Tracking](/docs/developer-guide/android-sdk/compliance/compliance#enabledisable-data-tracking)
## Logout
The application needs to notify the MoEngage SDK whenever the user is logged out of the application. Call the API whenever the user is logged out of the application to notify the SDK.
```kotlin Kotlin wrap theme={null}
MoECoreHelper.logoutUser(context)
```
```java Java theme={null}
MoECoreHelper.INSTANCE.logoutUser(context);
```
If the application is registering for a push token, it should pass the new push token to MoEngage SDK after the user logs out. For more information about passing push tokens, refer to [Push Configuration for Android SDK](/docs/developer-guide/android-sdk/push/basic/push-configuration).
# Tracking User Attributes
The SDK provides APIs to track commonly tracked user attributes like First Name, Last Name, Email-Id, etc. Please use the provided methods for tracking these attributes.
For more information on supported data types and data tracking policies, please refer to [Data Tracking Policies](https://www.moengage.com/docs/user-guide/data/key-concepts/data-tracking-policies).
```kotlin Kotlin wrap theme={null}
MoEAnalyticsHelper.setFirstName(context, "Jane")
MoEAnalyticsHelper.setLastName(context, "Doe")
MoEAnalyticsHelper.setUserName(context, "jane.doe")
MoEAnalyticsHelper.setLocation(context, GeoLocation(40.77, 73.98))
MoEAnalyticsHelper.setGender(context, UserGender.FEMALE)
MoEAnalyticsHelper.setMobileNumber(context, "+10000000000")
MoEAnalyticsHelper.setBirthDate(context, "1990-01-01T00:00:00.000Z")
MoEAnalyticsHelper.setEmailId(context, "jane.doe@example.com")
```
```java Java theme={null}
MoEAnalyticsHelper.INSTANCE.setFirstName(context, "Jane");
MoEAnalyticsHelper.INSTANCE.setLastName(context, "Doe");
MoEAnalyticsHelper.INSTANCE.setUserName(context, "jane.doe");
MoEAnalyticsHelper.INSTANCE.setLocation(context, new GeoLocation(40.77, 73.98));
MoEAnalyticsHelper.INSTANCE.setGender(context, UserGender.FEMALE);
MoEAnalyticsHelper.INSTANCE.setMobileNumber(context, "+10000000000");
MoEAnalyticsHelper.INSTANCE.setBirthDate(context, "1990-01-01T00:00:00.000Z");
MoEAnalyticsHelper.INSTANCE.setEmailId(context, "jane.doe@example.com");
```
For setting other User Attributes, use the generic method `setUserAttribute(key, value)`. The method accepts the following value types: `String`, `int`, `long`, `float`, `double`, `boolean`, `Date`, `GeoLocation`, `JSONObject`, `JSONArray`, and arrays of `int`, `long`, `float`, `double`, and `String`.
```kotlin Kotlin wrap theme={null}
// Tracking a String Attribute
MoEAnalyticsHelper.setUserAttribute(context,"locality", "SF")
// Tracking a Date Attribute
MoEAnalyticsHelper.setUserAttribute(context,"signedUpOn", Date())
// Tracking a location attribute
MoEAnalyticsHelper.setUserAttribute(context,"lastLocation", GeoLocation(40.77, 73.98))
// Tracking Array Attributes
MoEAnalyticsHelper.setUserAttribute(context,"int_array",arrayOf(1,2,3))
MoEAnalyticsHelper.setUserAttribute(context,"string_array",arrayOf("English","French"))
MoEAnalyticsHelper.setUserAttribute(context,"double_array",arrayOf(40.0,20.0))
MoEAnalyticsHelper.setUserAttribute(context,"json_array", JSONArray(listOf(1, 2, 3)))
MoEAnalyticsHelper.setUserAttribute(context,"json_object", JSONObject().put("key", "value"))
```
```java Java theme={null}
// Tracking a String Attribute
MoEAnalyticsHelper.INSTANCE.setUserAttribute(context,"locality", "SF");
// Tracking a Date Attribute
MoEAnalyticsHelper.INSTANCE.setUserAttribute(context, "signedUpOn", new Date());
// Tracking a location attribute
MoEAnalyticsHelper.INSTANCE.setUserAttribute(context,"lastLocation", new GeoLocation(40.77, 73.98));
// Tracking Array Attributes
MoEAnalyticsHelper.INSTANCE.setUserAttribute(context,"int_array",new int[]{1,2,3});
MoEAnalyticsHelper.INSTANCE.setUserAttribute(context,"string_array",new String[]{"English","French"});
MoEAnalyticsHelper.INSTANCE.setUserAttribute(context,"double_array",new double[]{1.5,2.5});
MoEAnalyticsHelper.INSTANCE.setUserAttribute(context,"json_array", new JSONArray());
MoEAnalyticsHelper.INSTANCE.setUserAttribute(context,"json_object", new JSONObject());
```
For more information about the detailed list of user attributes, refer to the [API reference](https://moengage.github.io/android-api-reference/core/com.moengage.core.analytics/-mo-e-analytics-helper/index.html).
## Reserved keywords for User Attributes
Below is the list of keys that should not be used when tracking user attributes.
* `USER_ATTRIBUTE_UNIQUE_ID`
* `USER_ATTRIBUTE_USER_EMAIL`
* `USER_ATTRIBUTE_USER_MOBILE`
* `USER_ATTRIBUTE_USER_NAME`
* `USER_ATTRIBUTE_USER_GENDER`
* `USER_ATTRIBUTE_USER_FIRST_NAME`
* `USER_ATTRIBUTE_USER_LAST_NAME`
* `USER_ATTRIBUTE_USER_BDAY`
* `USER_ATTRIBUTE_NOTIFICATION_PREF`
* `USER_ATTRIBUTE_OLD_ID`
* `MOE_TIME_FORMAT`
* `MOE_TIME_TIMEZONE`
* `USER_ATTRIBUTE_DND_START_TIME`
* `USER_ATTRIBUTE_DND_END_TIME`
* `MOE_GAID`
* `INSTALL`
* `UPDATE`
* `MOE_ISLAT`
* `status`
* `user_id`
* `source`
You cannot use "moe\_" as a prefix while naming events, event attributes, or user attributes. It is a system prefix and using it might result in periodic blacklisting without prior communication.
# Android SDK Deprecated Versions
Source: https://moengage.com/docs/developer-guide/android-sdk/deprecated-versions/deprecated-versions
Check the support status of MoEngage Android SDK major versions: which are current, which are still supported, and which are deprecated, along with deprecation dates.
Use this page to check the support status of MoEngage Android SDK major versions. It lists which versions are current, which are still supported, and which are deprecated, along with deprecation dates. This is the source of truth for Android SDK version status. For how the lifecycle works and what deprecation means for your app, see the [SDK Deprecation Policy](/docs/developer-guide/sdk-lifecycle-and-policies/sdk-deprecation-policy). To set up the Android SDK, see the [integration guide](/docs/developer-guide/android-sdk/sdk-integration/basic-integration/Install-Using-BOM).
## Version Support Status
### Status Definitions
| Status | Definition |
| -------------- | ----------------------------------------------------------------------------------------- |
| **Current** | The latest major version. It receives new features, fixes, and support. |
| **Supported** | An older major version within its 3-year support window. It continues to receive support. |
| **Deprecated** | A version outside its support window. It no longer receives fixes or support. |
| Major Version | Status | Deprecation Date | Notes |
| -------------------------- | ---------- | ---------------- | -------------------------------------------------------------------------------------------------------------- |
| 15.x | Current | TBD | Latest major version. Receives new features, fixes, and support. |
| 14.x | Supported | TBD | Receives support. |
| 13.x | Supported | TBD | Receives support. |
| 12.10.04 version and below | Deprecated | August 2027 | Support, debugging, and fixes are no longer available. Data continues to flow. Upgrade to the current version. |
MoEngage supports each major version for 3 years from its initial release; deprecation then takes effect the following August. See the [SDK Deprecation Policy — Support Window](/docs/developer-guide/sdk-lifecycle-and-policies/sdk-deprecation-policy#support-window) for details. This table is updated when MoEngage announces further deprecations. To receive advance notice, subscribe to the SDK newsletter and ensure your workspace administrator email is current.
## Understanding Deprecated Status
A deprecated version continues to send and receive data. MoEngage does not block traffic. After the deprecation date, MoEngage no longer debugs, fixes, or supports issues specific to that version, and the resolution for any issue is to upgrade to the current version. All fixes ship in the current version only and are not available on supported or deprecated versions. For the complete policy, see the [SDK Deprecation Policy](/docs/developer-guide/sdk-lifecycle-and-policies/sdk-deprecation-policy).
## Upgrading to a Supported Version
* Refer to the [Android SDK release notes](/docs/release-notes/sdks/android) to review changes across versions.
* Follow the [Android SDK release checklist](/docs/developer-guide/android-sdk/checklist/release-checklist) to plan and validate your upgrade.
* Contact your MoEngage Customer Success Manager (CSM) or the Support team for help planning an upgrade.
## Frequently Asked Questions
Apps on deprecated versions continue to run and data continues to flow. However, MoEngage no longer provides support, debugging, or fixes for deprecated versions. Upgrading to the current or a supported version using the [Android SDK release notes](/docs/release-notes/sdks/android) and the [Android SDK release checklist](/docs/developer-guide/android-sdk/checklist/release-checklist) ensures you receive continued support.
MoEngage sends advance notice 6 to 12 months before the deprecation date through email to workspace administrators, the quarterly SDK newsletter, in-dashboard notices, and SDK release notes. See the [SDK Deprecation Policy](/docs/developer-guide/sdk-lifecycle-and-policies/sdk-deprecation-policy) for the full notification schedule.
# In-App NATIV
Source: https://moengage.com/docs/developer-guide/android-sdk/in-app-messages/in-app-nativ
Display contextual in-app messages to your Android app users using the MoEngage In-App NATIV SDK.
In-App NATIV Campaigns target your users by showing a message while the user is using your app. They are very effective in providing contextual information and help to cross-sell/up-sell on desired screens of your app or/and on desired actions performed by the user in your app.
# Prerequisites
Add the following dependencies to your `app/build.gradle` before integrating in-apps:
* **MoEngage in-app module** — Integration using BOM is recommended. See [Install Using BOM](/docs/developer-guide/android-sdk/sdk-integration/basic-integration/Install-Using-BOM). With the BOM configured, version numbers are managed automatically.
* **Glide** — Starting in-app version **7.0.0**, the SDK requires [Glide](https://bumptech.github.io/glide/) to render images and GIFs in in-apps. If Glide is missing at runtime, in-apps containing images or GIFs crash.
```groovy build.gradle wrap theme={null}
dependencies {
...
implementation("com.moengage:inapp")
implementation("com.github.bumptech.glide:glide:4.16.0")
}
```
Once the BOM is configured, include the specific MoEngage modules required for the application. \
Note: Version numbers are not required for these dependencies; the BOM automatically manages them.
# Display InApp
MoEngage refreshes the list of eligible In-App campaigns via a meta API call. During an active session, the list refreshes when the app returns from the background to the foreground (at most once every 15 minutes), and immediately when a new session starts or the app is killed and relaunched.
MoEngage can not show the InApp by default, and the app should call the following method in the places where necessary to show the InApps to the user. We recommend adding this method in onStart() of your activity or onResume() of your fragment. [MoEInAppHelper.getInstance().showInApp(context)](https://moengage.github.io/android-api-reference/inapp/com.moengage.inapp/-mo-e-in-app-helper/show-in-app.html)
```kotlin Kotlin wrap theme={null}
MoEInAppHelper.getInstance().showInApp(context)
```
```java Java theme={null}
MoEInAppHelper.getInstance().showInApp(context);
```
# Display Nudges
Starting with version \*\*7.0.0,\*\*MoEngage InApp SDK supports displaying Non-Intrusive nudges.
MoEngage can not show the Nudges by default, and the app should call the following method in the places where necessary to show the Nudges to the user. We would recommend you add this method in onStart() of your activity or onResume() of your fragment. [MoEInAppHelper.getInstance().showNudge(context)](https://moengage.github.io/android-api-reference/inapp/com.moengage.inapp/-mo-e-in-app-helper/show-nudge.html)
```kotlin Kotlin wrap theme={null}
MoEInAppHelper.getInstance().showNudge(context)
```
```java Java theme={null}
MoEInAppHelper.getInstance().showNudge(context);
```
# Handling Configuration change
Starting SDK version **11.4.00,** in-apps are supported in both portrait and landscape modes. SDK internally handles in-app display on orientation change when the activity restart is handled by the system.
In case your activity is handling the configuration change by itself, you have to notify the SDK by invoking [*MoEInAppHelper.getInstance().onConfigurationChanged()*](https://moengage.github.io/android-api-reference/inapp/com.moengage.inapp/-mo-e-in-app-helper/on-configuration-changed.html) API for SDK to redraw the in-app when the activity receives *onConfigurationChanged()* callback from the framework.
```kotlin Kotlin wrap theme={null}
override fun onConfigurationChanged(newConfig: Configuration) {
super.onConfigurationChanged(newConfig)
MoEInAppHelper.getInstance().onConfigurationChanged()
}
```
```java Java theme={null}
@Override public void onConfigurationChanged(@NonNull Configuration newConfig) {
super.onConfigurationChanged(newConfig);
MoEInAppHelper.getInstance().onConfigurationChanged();
}
```
# Contextual InApp
You can restrict the in-apps based on the user's context in the application, apart from restricting InApp campaigns on a specific screen/activity. To set the user's context in the application, use [*setInAppContext()*](https://moengage.github.io/android-api-reference/inapp/com.moengage.inapp/-mo-e-in-app-helper/set-in-app-context.html) API, as shown below.
## Set Context
Call the below method in the *onStart()* of your *Activity* or *Fragment* before calling *showInApp().*
```kotlin Kotlin wrap theme={null}
MoEInAppHelper.getInstance().setInAppContext(setOf("context1", "context2", "context3"))
```
```java Java theme={null}
Set inAppContext = new HashSet<>();
inAppContext.add("context1");
inAppContext.add("context2");
inAppContext.add("context3");
MoEInAppHelper.getInstance().setInAppContext(inAppContext);
```
The context is not the same context as [Android Context](https://developer.android.com/reference/android/content/Context?hl=en). This user's context in the application flow.
## Reset Context
Once the user is moving out of the context, use the [*restInAppContext()*](https://moengage.github.io/android-api-reference/inapp/com.moengage.inapp/-mo-e-in-app-helper/reset-in-app-context.html) API to reset/clear the existing context.
```kotlin Kotlin wrap theme={null}
MoEInAppHelper.getInstance().resetInAppContext()
```
```java Java theme={null}
MoEInAppHelper.getInstance().resetInAppContext();
```
Code example below:
```kotlin Kotlin wrap theme={null}
// Activity
class MyCustomActivity: Activity() {
override fun onStart() {
super.onStart()
MoEInAppHelper.getInstance().setInAppContext(setOf("context1", "context2"))
MoEInAppHelper.getInstance().showInApp(this)
}
override fun onStop() {
super.onStop()
MoEInAppHelper.getInstance().resetInAppContext()
}
}
// Fragment
class MyCustomFragment: Fragment() {
override fun onStart() {
super.onStart()
// Fragment's onStart code
// context1 and context2 can be changes as per screen/requirement
MoEInAppHelper.getInstance().setInAppContext(setOf("context1", "context2"))
MoEInAppHelper.getInstance().showInApp(this)
}
override fun onStop() {
super.onStop()
// Fragment's onStop code
MoEInAppHelper.getInstance().resetInAppContext()
}
}
```
```java Java theme={null}
// Activity
public class MyCustomActivity extends Activity {
@Override
protected void onStart() {
super.onStart();
// Activity's custom onStart logic here
Set inAppContext = new HashSet<>();
inAppContext.add("context1"); // Change the string name as per your requirement
inAppContext.add("context2"); // Change the string name as per your requirement
MoEInAppHelper.getInstance().setInAppContext(inAppContext);
MoEInAppHelper.getInstance().showInApp(this)
}
@Override
protected void onStop() {
super.onStop();
// Activity's custom onStop logic here
MoEInAppHelper.getInstance().resetInAppContext();
}
}
// Fragment
public class MyCustomFragment extends Fragment {
// Other Fragment code
@Override
public void onStart() {
super.onStart();
// Fragment's onStart code here
Set inAppContext = new HashSet<>();
inAppContext.add("context1");
inAppContext.add("context2");
MoEInAppHelper.getInstance().setInAppContext(inAppContext);
MoEInAppHelper.getInstance().showInApp(this)
}
@Override
public void onStop() {
super.onStop();
MoEInAppHelper.getInstance().resetInAppContext();
}
}
```
# WebView Customization
Starting with In-App SDK version **9.6.0**, the SDK supports customizing the underlying WebView used to render HTML In-App messages. This allows for modifying the WebView settings, adding custom JavaScript interfaces, and implementing other configurations to the WebView used to render HTML In-Apps.
To customize the WebView, you must invoke the `MoEInAppHelper.getInstance().setInAppWebViewCustomizer()` API. Any custom settings defined using this API will override the default MoEngage In-App WebView settings.
```kotlin Kotlin wrap theme={null}
MoEInAppHelper.getInstance().setInAppWebViewCustomizer {
addJavascriptInterface(MyBridge(), "bridge")
settings.mediaPlaybackRequiresUserGesture = false
}
```
```java Java theme={null}
MoEInAppHelper.getInstance().setInAppWebViewCustomizer(
new kotlin.jvm.functions.Function1() {
@Override
public kotlin.Unit invoke(InAppWebView webView) {
WebSettings settings = webView.getSettings();
settings.setMediaPlaybackRequiresUserGesture(false);
webView.addJavascriptInterface(new CustomJavaScriptBridge(), "myBridge");
return kotlin.Unit.INSTANCE;
}
}
);
```
# Self-Handled InApps
Self-handled In-Apps are messages that the SDK delivers to the application, and the application builds the UI using the SDK's delivered payload.
## Single Self-Handled InApps
To get the self-handled in-app, use the below API.
```kotlin Kotlin wrap theme={null}
MoEInAppHelper.getInstance().getSelfHandledInApp(context, listener)
```
```java Java theme={null}
MoEInAppHelper.getInstance().getSelfHandledInApp(context, listener);
```
The **listener** is an instance of [*SelfHandledAvailableListener.*](https://moengage.github.io/android-api-reference/inapp/com.moengage.inapp.listeners/-self-handled-available-listener/index.html)
This method should be called in the *onResume()* of your Fragment or *onStart()* of your activity.\
The above method is asynchronous and does not return the payload immediately, once the payload is available [*onSelfHandledAvailable(), the listener callback*](https://moengage.github.io/android-api-reference/inapp/com.moengage.inapp.listeners/-self-handled-available-listener/on-self-handled-available.html) would be called with the payload.
### Event-Triggered Self Handled InApps
To get a callback for an event triggered, implement *SelfHandledAvailableListener* and register for a listener using [*MoEInAppHelper.getInstance().setSelfHandledListener()*](https://moengage.github.io/android-api-reference/inapp/com.moengage.inapp/-mo-e-in-app-helper/set-self-handled-listener.html). SDK will notify the registered listener once the campaign is available.
We recommend registering this listener in the *onCreate()* of the *Application* class if the trigger event can happen on multiple screens.
## Multiple Self-Handled InApps
* This feature requires a minimum catalog version **4.5.0**
* Event-triggered multiple self-handled inapps are not supported.
Fetch Multiple Self Handled Campaigns using [*getSelfHandledInApps()*](https://moengage.github.io/android-api-reference/inapp/com.moengage.inapp/-mo-e-in-app-helper/get-self-handled-in-app.html). The MoEngage SDK will return up to 5 campaigns(in the order of campaign priority set at the time of campaign creation) in the campaigns available callback method [*onCampaignsAvailable()*](https://moengage.github.io/android-api-reference/inapp/com.moengage.inapp.listeners/-self-handled-campaigns-available-listener/on-campaigns-available.html)**.**
```kotlin Kotlin wrap theme={null}
MoEInAppHelper.getInstance().getSelfHandledInApps(context, listener)
```
```java Java theme={null}
MoEInAppHelper.getInstance().getSelfHandledInApps(context, listener);
```
The *listener* is an instance of [*SelfHandledCampaignsAvailableListener*](https://moengage.github.io/android-api-reference/inapp/com.moengage.inapp.listeners/-self-handled-campaigns-available-listener/index.html)[.](https://moengage.github.io/android-api-reference/inapp/com.moengage.inapp.listeners/-self-handled-available-listener/index.html)
This method should be called in the *onResume()* of your Fragment or *onStart()* of your activity.\
The above method is asynchronous and does not return the payload immediately, once the payload is available [*onCampaignsAvailable() callback*](https://moengage.github.io/android-api-reference/inapp/com.moengage.inapp.listeners/-self-handled-campaigns-available-listener/on-campaigns-available.html) of the listener would be called with the payload.
### Tracking Statistics for Multiple Self-Handled In-Apps
The *onCampaignsAvailable()* callback method returns [*SelfHandledCampaignsData*](https://moengage.github.io/android-api-reference/inapp/com.moengage.inapp.model/-self-handled-campaigns-data/index.html), which contains a list of [*SelfHandledCampaignData*](https://moengage.github.io/android-api-reference/inapp/com.moengage.inapp.model/-self-handled-campaign-data/index.html) objects. The statistics for each *SelfHandledCampaignData* object must be tracked individually below APIs.
### Fetching Contextual Multiple Self-Handled InApps
To fetch contextual multiple self-handled inapps, set the inapp contexts using [*setInAppContext()*](https://www.moengage.com/docs/developer-guide/android-sdk/in-app-messages/in-app-nativ#set-context) before calling \*[getSelfHandledInApps()](https://moengage.github.io/android-api-reference/inapp/com.moengage.inapp/-mo-e-in-app-helper/get-self-handled-in-app.html).\*This will return a list of contextual and non-contextual inapps(in the order of campaign priority set at the time of campaign creation).
### Campaign Selection Logic
* **Default Limit**: By default, only 5 campaigns will be fetched.
* **Priority-Based Selection**: Campaigns are delivered based on their priority and last updated time. It checks for priority first and then checks the last updated time on conflicting priorities
* **Exclusion criteria**: Campaigns are only excluded based on specific rules like frequency capping, eligibility criteria, campaign status, or priority limits.
**Example Scenario:** If you have 6 campaigns with different priorities, published time and contexts:
* Context 1: Campaign 1 (P0, T2), Campaign 2 (P1, T3), Campaign 3 (P2, T6)
* Context 2: Campaign 4 (P0, T1), Campaign 5 (P1, T5)
* Context 3: Campaign 6 (P0, T4)
Following campaigns will be delivered in this order: \[Campaign 4, Campaign 1, Campaign 6, Campaign 2, Campaign 5]
**Selection Algorithm:**
1. Filter campaigns by user eligibility and targeting criteria
2. Sort by campaign priority (P0, P1, P2, etc.)
3. For campaigns with same priority, sort by most recent update timestamp
4. Return top 5 campaigns
#### **Best Practices for Campaign Organization for multiple self handled campaigns**
For optimal performance across multiple contexts on a single page, organize your campaigns like this:
* Context 1 (Homepage): Campaign A (P0), Campaign B (P1)
* Context 2 (Product): Campaign C (P0), Campaign D (P1)
* Context 3 (Checkout): Campaign E (P0)
This ensures each context has relevant campaigns without hitting the 5-campaign limit.
Also, make sure that you set the priority of the campaigns you want to fetch accordingly, because the method will fetch all self-handled campaigns regardless of whether they are context-based or not.
## Tracking Statistics for Self-Handled In-Apps
The application must notify MoEngage SDK whenever the In-App messages are displayed, clicked on, or dismissed, as the application controls these actions. The following methods are called to notify the SDK. The data object [*SelfHandledCampaignData*](https://moengage.github.io/android-api-reference/inapp/com.moengage.inapp.model/-self-handled-campaign-data/index.html) provided to the application in the callback for self-handled in-app should be passed as a parameter to the following APIs.
```kotlin Kotlin wrap theme={null}
// call whenever in-app is shown
MoEInAppHelper.getInstance().selfHandledShown(context, data)
// call whenever in-app is clicked
MoEInAppHelper.getInstance().selfHandledClicked(context, data)
// call whenever in-app is dismissed
MoEInAppHelper.getInstance().selfHandledDismissed(context, data)
```
```java Java theme={null}
// call whenever in-app is shown
MoEInAppHelper.getInstance().selfHandledShown(context, data);
// call whenever in-app is clicked
MoEInAppHelper.getInstance().selfHandledClicked(context, data);
// call whenever in-app is dismissed
MoEInAppHelper.getInstance().selfHandledDismissed(context, data);
```
For more information, refer to the [API documentation](https://moengage.github.io/android-api-reference/inapp/com.moengage.inapp/-mo-e-in-app-helper/index.html#-59857046%2FFunctions%2F434681417).
# In-Apps Callback
## Lifecycle callback
To get callbacks whenever an InApp campaign is shown or dismissed, implement the [*InAppLifeCycleListener*](https://moengage.github.io/android-api-reference/inapp/com.moengage.inapp.listeners/-in-app-life-cycle-listener/index.html) and register for the callbacks using [*MoEInAppHelper.getInstance().addInAppLifeCycleListener()*](https://moengage.github.io/android-api-reference/inapp/com.moengage.inapp/-mo-e-in-app-helper/add-in-app-life-cycle-listener.html).
## Click Callback
To handle user navigation or custom action, SDK provides a callback whenever an in-app widget is clicked with either Navigation or Custom action. To get callbacks implement the [*OnClickActionListener*](https://moengage.github.io/android-api-reference/inapp/com.moengage.inapp.listeners/-on-click-action-listener/index.html) interface and register for the callbacks using [*MoEInAppHelper.getInstance().setClickActionListener()*](https://moengage.github.io/android-api-reference/inapp/com.moengage.inapp/-mo-e-in-app-helper/set-click-action-listener.html).
# Blocking InApps on Screens
Additionally, you can block in-app on a specific screen or handle the status bar visibility using the [InAppConfig](https://moengage.github.io/android-api-reference/inapp/com.moengage.inapp.config/index.html) object and pass it to the SDK using the [MoEngage.Builder](https://moengage.github.io/android-api-reference/core/com.moengage.core/-mo-engage/-builder/index.html) object. Use the [configureInApps()](https://moengage.github.io/android-api-reference/core/com.moengage.core/-mo-engage/-builder/configure-in-apps.html) API to pass on the configuration to the SDK.
```kotlin Kotlin wrap theme={null}
// List of activity classes on which in-app should not be shown
val inAppOptOut = mutableListOf()
inAppOptOut.add(SplashActivity::class.java.name)
val moengage = MoEngage.Builder(
application = application,
appId = "YOUR_WORKSPACE_ID",
dataCenter = DataCenter.DATA_CENTER_X
)
.configureInApps(InAppConfig(inAppOptOut, true))
.build()
MoEngage.initialiseDefaultInstance(moengage)
```
```java Java theme={null}
ArrayList inAppOptOut = new ArrayList<>();
inAppOptOut.add(SplashActivity.class.getName());
MoEngage.Builder builder = new MoEngage.Builder(application, "YOUR_WORKSPACE_ID", DataCenter.DATA_CENTER_X)
.configureInApps(new InAppConfig(inAppOptOut, true));
MoEngage.initialiseDefaultInstance(builder.build());
```
# Testing In-App
Refer to this [link](https://www.moengage.com/docs/user-guide/campaigns-and-channels/in-app-message/create/test-your-in-app-campaign) to read more about how to create and test in-apps.
# Implementing Embedded Nudges (Deprecated)
Starting InApp version **7.0.0,** embedded nudges are no longer supported.
Nudges are non-disruptive messages which can be placed anywhere in the activity.
Add the following code in the activity/fragment layout file.
```XML XML wrap theme={null}
```
## Using in an Activity
Get an instance of the nudge view in the **onCreate()** and initialize the nudge view in the **onStart()** of the Activity.
## Using in a Fragment
Get an instance of the nudge view in the **onCreateView()** and initialize the nudge view in the **onResume()** of the fragment.
Use the below code to get the instance of the ***NudgeView*** and initialize it.
```kotlin Kotlin wrap theme={null}
// get instance of the view
val nudge = findViewById(R.id.nudge)
// initialize
nudge.initialiseNudgeView(activity)
```
```java Java theme={null}
// get instance of the view
NudgeView nv = (NudgeView)findViewById(R.id.nudge);
// initialize
nv.initialiseNudgeView(getActivity());
```
# FAQs
* For In-App SDK version 9.6.0 and above, refer to the [WebView Customization](https://www.moengage.com/docs/developer-guide/android-sdk/in-app-messages/in-app-nativ#webview-customization).
* If you are using In-App SDK version below 9.6.0, perform the following steps:
* Android's default WebView does not support file inputs. To enable this functionality, trigger a Custom Action from your HTML and handle it natively with an *OnClickActionListener*.\
Within the listener, launch your native file picker or camera logic. Ensure your app requests the required runtime permissions, such as CAMERA and READ\_EXTERNAL\_STORAGE.
Example:
```kotlin Kotlin wrap theme={null}
val listener = OnClickActionListener { clickData ->
if (clickData.action.actionType == ActionType.CUSTOM_ACTION &&
(clickData.action as CustomAction).keyValuePairs["action"] == "uploadPhoto")
{
openImageChooser() // Your native camera/gallery function
true // Indicates the action was handled
} else {
false
}
}
```
* For iOS, file inputs work by default. However, for a consistent cross-platform implementation, we recommend using the same Custom Action approach.
# Migrating from addon-inbox 6.0.2
Source: https://moengage.com/docs/developer-guide/android-sdk/migration/migrating-from-addon-inbox-602
Migrate from addon-inbox 6.0.2 to the updated inbox-core and inbox-ui modules in MoEngage Android SDK.
This migration is required only if you are using *addon-inbox 6.0.2* or below and migrating to *11.2.00* or above version of moe-android-sdk.
Since the early days of MoEngage, we have provided `addon-inbox` artifact for using Notification Center in your application. We have revamped the module and broken down the module into multiple modules to make it more robust, lightweight, and scalable for future improvements/enhancements.
# Updated Artifacts
`inbox-core` - Module contains APIs and helper methods to build an inbox. Provides APIs to fetch messages, track clicks, etc
`inbox-ui` - Module contains the user interface for the inbox module and helper methods for customization of the UI.
# Using SDK Notification Center
If you are using the UI provided by the SDK you need to include the `inbox-ui` module in your application. Key changes you would notice here apart from the artifact name update
* RecyclerView used instead of ListView
* Updated UI
* API updates for customizing the UI
Refer to the updated [MoEngage's default Notification Center](https://www.moengage.com/docs/developer-guide/android-sdk/push/optional/notification-center) for details on how to use the default UI and possible customization.
# Self handled Notification Center
If you were building your own Notification Center consuming the data from MoEngage we have made the APIs simpler for you and made the SDK lighter. Integrate the `inbox-core` module.
Refer to the [Self Handled Notification Center](https://www.moengage.com/docs/developer-guide/android-sdk/push/optional/notification-center#self-handled-notification-center) for updated APIs
# Update the packages:
While we have revamped the modules we have tried to maintain some of the classes to avoid re-integration wherever feasible. Though we have not changed the APIs as such we have moved it to a new package for better integration in the future.\
Refer to the below table for updated packages.
| Then | Now |
| -------------------------------------------------------------- | --------------------------------------------------------------- |
| com.moengage.addon.inbox.MoEInboxHelper | com.moengage.inbox.core.MoEInboxHelper |
| com.moengage.addon.inbox.listener. OnMessagesAvailableListener | com.moengage.inbox.core.listener. OnMessagesAvailableListener |
| com.moengage.addon.inbox. InboxMessageClickCallback | com.moengage.inbox.ui.listener. OnMessageClickListener |
| com.moengage.addon.inbox.MoEInboxActivity | com.moengage.inbox.ui.view\.InboxActivity |
| com.moengage.addon.inbox.InboxFragment | com.moengage.inbox.ui.view\.InboxFragment |
| com.moengage.addon.inbox.InboxManager .ViewHolder | com.moengage.inbox.ui.adapter.ViewHolder |
| com.moengage.addon.inbox.InboxManager .InboxAdapter | com.moengage.inbox.ui.adapter.InboxAdapter |
# Migration from 4.x to 5.x (One time activity)
Source: https://moengage.com/docs/developer-guide/android-sdk/migration/migration-from-4x-to-5x-one-time-activity
Migrate your MoEngage Android SDK integration from version 4.x to 5.x with updated receivers and APIs.
**Historical migration guide.** This page covers the MoEngage Android SDK 4.x → 5.x migration, which predates the GCM → FCM transition. Skip it if you are already on SDK 5.x or later. Use the current [SDK Integration](/docs/developer-guide/android-sdk/sdk-integration/basic-integration/configuring-build-settings) and version-specific migration guides instead.
Follow the installation steps mentioned [here](/docs/developer-guide/android-sdk/sdk-integration/basic-integration/Install-Using-BOM).
Changes required to migrate to 5.x are minimum but some structural changes require you to remove some lines of code. This had to be done to make it loosely coupled and easy to integrate
* No change in Manifest permissions
* Basic integration points remain same
* Changes in receivers & services
* Deprecated few APIs and provided alternatives
* Inbox moved out of Main SDK and added to add-on lib
If using Play Services 7.3 **No Changes in the following receivers**.
```XML XML wrap theme={null}
{/* MOENGAGE RECEIVER FOR RECEIVING GCM BROADCAST MESSAGES */}
{/* MOENGAGE RECEIVER FOR RECEIVING INSTALLATION INTENT */}
```
If using Google Play Services 7.5
```XML XML wrap theme={null}
```
# Remove the following receivers
```Java Java wrap theme={null}
{/* MOENGAGE RECEIVER FOR RECEIVING PACKAGE UPDATED INTENT */}
{/* MOENGAGE SERVICE PROCESSING GCM MESSAGES */}
{/* MOENGAGE RECEIVER FOR INTERNAL PURPOSE */}
{/* MOENGAGE RECEIVER FOR TRIGGERING INTERACTION DATA SYNC */}
```
# Add the following Receivers
```XML XML wrap theme={null}
```
# Add the following Provider
```XML XML wrap theme={null}
```
# Add the \ following tags
```XML XML wrap theme={null}
{/* MANDATORY FIELD: APP ID AS SEEN ON MOENGAGE DASHBOARD APP SETTINGS PAGE */}
{/* MANDATORY FIELD: SENDER ID , i.e. THE PROJECT NUMBER AS MENTIONED ON GOOGLE CLOUD CONSOLE PROJECTS PAGE */}
{/* MANDATORY FIELD: THE NOTIFICATION SMALL ICON WHICH WILL BE USED TO SET TO NOTIFICATIONS POSTED */}
{/* MANDATORY FIELD: THE NOTIFICATION LARGE ICON WHICH WILL BE USED TO SET TO NOTIFICATIONS POSTED */}
{/* OPTIONAL FIELD: THE NOTIFICATION TYPE WHICH WILL BE USED, SINGLE OR MULTIPLE. DEFAULT BEHAVIOR IS SINGLE */}
{/* OPTIONAL FIELD: THE NOTIFICATION TONE THAT WILL BE USED. IF NOT SET WILL PLAY THE DEFAULT SOUND */}
```
# Delete the following code
```Java Java wrap theme={null}
//Delete the INITIALISATION METHOD CALL. THIS IS DEPRECATED AND NOT REQUIRED ANY LONGER
helper.initialize( GCM_SENDER_ID, MOE_APP_ID);
//Delete the REGISTER METHOD CALL. THIS IS DEPRECATED AND NOT REQUIRED ANY LONGER
helper.Register(R.drawable.ic_launcher);
```
# Add the following code (optional)
Add this only if you support the change in orientation.
```Java Java wrap theme={null}
/*
* (non-Javadoc)
*
* @see android.app.Activity#onSaveInstanceState(android.os.Bundle)
*/
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
mHelper.onSaveInstanceState(outState);
}
/*
* (non-Javadoc)
*
* @see android.app.Activity#onRestoreInstanceState(android.os.Bundle)
*/
@Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
mHelper.onRestoreInstanceState(savedInstanceState);
}
```
# Inbox Users
Change the following:
Old declaration
```XML XML wrap theme={null}
{/* MOENGAGE INBOX ACTIVITY DECLARATION */}
```
Change to
```XML XML wrap theme={null}
```
# Add the Install/Update Differentiator
Add the install update differentiator as mentioned [here](https://www.moengage.com/docs/developer-guide/android-sdk/data-tracking/basic/track-install-or-update).
In case you have any issues, please do contact the Custom Success Managers who will help you out
# Migration to 10.x.xx
Source: https://moengage.com/docs/developer-guide/android-sdk/migration/migration-to-10xxx
Migrate to MoEngage Android SDK 10.x.xx with updated APIs, removed GCM support, and behavioral changes.
# Migrating from Manifest based integration
In version SDK version `9.0.00` we introduced a new way of integrating the MoEngage SDK via Java/Kotlin code and deprecated integration using metadata in the Manifest file. Starting version `10.0.00` we are removing support for integration which uses Manifest metadata. Refer to the [documentation](https://www.moengage.com/docs/developer-guide/android-sdk/migration/moving-from-manifest-to-code-based-integration) to learn more on the migration to code-based integration.
# Behavioral Changes
* Self-Handled in-app delivered on the Main thread
* Removed support for GCM
* Removed support for Baidu
* For showing gifs in in-apps SDK was dependent on Fresco before SDK version `10.0.00` Starting from SDK version `10.0.00` SDK uses Glide to show gifs. Make sure you add Glide as a dependency in your application if you wish to use gifs.
* "nav\_provier", "nav\_source", will no longer be present in the push payload or deep-link link URL.
* If sender id is provided for while initializing the SDK it will be used for token registration instead of the default sender id in the `google-services.json` file.
* InApp Callbacks - InApp Callbacks listener is now a concrete class rather than an interface.
* InstallReceiver removed from the SDK. If you have added `com.moe.pushlibrary.InstallReceiver` in the manifest please remove.
# Update removed APIs
In version `10.0.00` of the SDK, we have removed many of the APIs which were long deprecated. If you are still using the deprecated APIs you have to update to the new APIs. Below is a table mapping the equivalents of the removed APIs. Some of the APIs might not have an alternate API as the functionality might have been handled internally or no longer supported.
| Deprecated API | Replacement API |
| ---------------------------------------------------------------- | --------------------------------------------------------------- |
| MoEngage.Builder#setTrackingOptOut(List) | MoEngage.Builder#optOutActivityTracking(List) |
| MoEngage.Builder#setInAppOptOut(List) | MoEngage.Builder#optOutInAppFromActivity (List) |
| MoEHelper#setFlushInterval(long) | MoEngage.Builder#setFlushInterval(long) |
| MoEHelper#setExistingUser(boolean) | MoEHelper#setAppStatus(AppStatus) |
| MoEHelper#optOutOfAdIdCollection (Context, boolean) | MoEngage.Builder#optOutGAIDCollection() |
| MoEHelper#optOutOfLocationTracking (Context, boolean) | MoEngage.Builder#optOutLocationTracking() |
| MoEHelper#optOutOfGeoFences (Context, boolean) | MoEngage.Builder#optOutGeoFence() |
| MoEHelper#setLogLevel(int) | MoEngage.Builder#setLogLevel(int) |
| MoEHelper#setLogStatus(boolean) | MoEngage.Builder#enableLogsForSignedBuild() |
| MoEHelper#optOutOfAndroidIdCollection(Context, boolean) | MoEngage.Builder#optOutAndroidIdCollection() |
| MoEHelper#optOutOfOperatorNameCollection (Context, boolean) | MoEngage.Builder#optOutCarrier NameCollection() |
| MoEHelper#optOutOfDeviceAttributeCollection (Context, boolean) | MoEngage.Builder#optOutDevice AttributeCollection() |
| MoEHelper#redirectDataToRegion(int) | MoEngage.Builder#redirectDataToRegion (MoEngage.DATA\_REGION) |
| MoEHelper#setPeriodicFlushState(boolean) | MoEngage.Builder#optOutPeriodicFlush() |
| PushManager#optoutBackStackBuilder(Boolean) | MoEngage.Builder#optOutBackStackBuilder() |
| MoEPushCallBacks.OnMoEPushReceiveListener | PushMessageListener#onNotificationReceived() |
| MoEPushCallBacks.OnMoEPushNavigationAction | PushMessageListener#onHandleRedirection() |
| MoEPushCallbacks.OnMoEPushClickListener | PushMessageListener#onHandleRedirection() |
| MoEPushCallBacks.OnMoEPushClearedListener | PushMessageListener#onNotificationCleared() |
| MoEPushCallBacks#setOnMoEPushReceiveListener | MoEPushHelper#setMessageListener\|(PushMessageListener) |
| MoEPushCallBacks#setOnMoEPush NavigationAction | MoEPushHelper#setMessageListener( PushMessageListener) |
| MoEPushCallBacks#setOnMoEPush ClearedListener | MoEPushHelper#setMessageListener (PushMessageListener) |
| MoEHelper#setBirthDate(String) | MoEHelper#setBirthDate(Date) |
| PushManager#optOutMoEngageExtras(boolean) | Not required any more |
| MoEHelper#showInAppIfAny(boolean) | Not required any more |
| MoEHelper#autoIntegrate(Application) | Not required any more |
| MoEHelper#onStart(Activity) | Not required any more |
| MoEHelper#onStop(Activity) | Not required any more |
| MoEHelper#onResume(Activity) | Not required any more |
| MoEHelper#onFragmentStart(Activity, String) | Not required any more |
| MoEHelper#onFragmentStop(Activity, String) | Not required any more |
| MoEHelper#optOutOfIMEICollection (Context, boolean) | Not required any more |
| MoEngage.Builder#optOutMoEngageExtras() | Not required any more |
| MoEngage.Builder#enableInstantApp() | Merged with MoEngage.Builder#setSenderId() |
| MoEngage.Builder#enableBaiduPush(String) | Baidu Push not supported. |
# APIs deprecated in 10.0.00
In version `10.0.00` we have deprecated a few APIs to improve performance and better support in the future. Below is the list of deprecated APIs and their alternatives, please update to the latest methods.
| Deprecated API | Replacement API |
| ----------------------------------------- | ----------------------------------------------------- |
| MoEngage.Builder#setNotificationType(int) | MoEngage.Builder#enableMultipleNotificationInDrawer() |
| PayloadBuilder | Properties |
# Migration To Maven Central
Source: https://moengage.com/docs/developer-guide/android-sdk/migration/migration-to-maven-central
Migrate your MoEngage Android SDK dependencies from JCenter to Maven Central.
JFrog recently announced that they are making important changes that will impact users of Bintray, JCenter. We as developers would not be able to publish new packages on Bintray after 31st March 2021 and existing packages wouldn't be available for use after 1st February 2022.\
Checkout out the [blog post](https://jfrog.com/blog/into-the-sunset-bintray-jcenter-gocenter-and-chartcenter/) for more details.
We at MoEngage have been publishing our Android SDK/artifacts to Jcenter for the past few years and have moved to publish artifacts to Maven Central. Going forward all the artifacts would be available via Maven Central only.
# How does it affect you?
Since existing packages are available for download till 1st February 2022 the shutdown will not affect you immediately. Though we strongly recommend you migrate to the version published on maven central(`11.0.04` or above) at the earliest to avoid any issues.
The versions mentioned are the latest version at the time of publishing this document(1st March 2021). Refer to the Release Notes of the respective framework.
# Migration for 11.x.xx
If you are using the 11.x.xx version of the MoEngage SDK you would just need to change the version to `11.0.04` or above to move to maven central Artifacts.\
Make sure you update the other unbundled/dependent MoEngage SDKs or modules(like Push Amp Plus, Push Templates, etc.) to the compatible version.\
Below is the list of compatible versions for `11.0.04`, 1st version on maven central.
| Artifact | Version |
| ----------------- | ------- |
| moe-android-sdk | 11.0.04 |
| addon-inbox | 6.0.2 |
| cards | 2.0.02 |
| hms-pushkit | 2.0.02 |
| push-amp-plus | 3.0.02 |
| rich-notification | 2.0.03 |
| geofence | 1.0.02 |
# Migration for 10.x.xx
Update to the latest SDK version. Because this spans multiple major versions, you must make additional code changes beyond the version bump in your `build.gradle`. Step through the migration guides in order:
1. [Updating to 11.x.xx from 10.x.xx](/docs/developer-guide/android-sdk/migration/updating-to-11xxx-from-10xxx)
2. [Updating to 12.x.xx from 11.x.xx](/docs/developer-guide/android-sdk/migration/updating-to-12xxx-from-11xxx)
For the latest version and full version history, see the [Android SDK release notes](/docs/developer-guide/release-notes/android-sdk). Update the other unbundled or dependent MoEngage modules (Push Amp Plus, Push Templates, and so on) to compatible versions as well.
In case, you are not able to update to `11.x.xx` due to some technical limitations or bandwidth issues we have uploaded the last `10.x.xx` version along with the add-on modules on Maven Central. You can update to the below versions.
| Artifact | Version |
| ----------------- | ------- |
| moe-android-sdk | 10.6.01 |
| addon-inbox | 5.3.1 |
| cards | 1.2.01 |
| hms-pushkit | 1.2.01 |
| push-amp-plus | 2.2.01 |
| rich-notification | 1.2.02 |
We strongly recommend you update to the latest version instead of 10.6.01 to take advantage of all the latest features/improvements/optimizations we have made.
# Migration for below 10.x.xx
We recommend you update to the latest version, we will not be publishing/moving 9.x.xx or below to maven central. Since you would be updating 2 major versions we recommend you go through the documentation and update accordingly.
# Cross-Platform Frameworks
MoEngage provides support for a number of cross-platform frameworks like React-Native, Flutter, Unity, Cordova, etc. Our Cross-Platform frameworks are dependent on the above-mentioned native(Java/Kotlin) Android SDK and equally affected by the Jcenter shutdown. We have updated our Cross-Platform plugins or packages to use artifacts published on Maven Central instead of Jcenter.\
Below are the latest version of the plugins.
| Framework Name | Version |
| -------------- | ------- |
| React-Native | 7.0.0 |
| Flutter | 3.0.0 |
| Flutter Inbox | 2.0.0 |
| Cordova | 7.0.0 |
| Unity | 2.0.0 |
The above version is built on top of `11.x.xx` and we strongly recommend you to use the above versions or above.
In case, you are not able to update to `11.x.xx` due to some technical limitations or bandwidth issues we have uploaded the last `10.x.xx` version along with the add-on modules on Maven Central. You can update to the below versions. These versions are built on top of `10.6.01` the `10.x.xx` version on Maven Central.
| Framework Name | Version |
| -------------- | ------- |
| React-Native | 6.1.7 |
| Flutter | 2.0.3 |
| Flutter Inbox | 1.0.2 |
| Cordova | 6.1.4 |
| Unity | 1.3.1 |
# Segment Integration
We recommend you migrate to the version published on maven central(`5.1.00` or above) at the earliest to avoid any issues.
# Migration for 5.x.xx
If you are using the 5.x.xx version of the MoEngage SDK you would just need to change the version to `5.1.00` or above to move to maven central Artifacts.\
Make sure you update the other unbundled/dependent MoEngage SDKs or modules(like Push Amp Plus, Push Templates, etc.) to the compatible version.
# Migration for 4.x.xx
Update to the latest SDK version `5.1.00` or above. Since it would be a major version update there might be additional code changes required apart from changing the version number in your gradle file(s). Refer to the [Updating to 11.x.xx](/docs/developer-guide/android-sdk/migration/updating-to-11xxx-from-10xxx) document and [Release Notes](/docs/developer-guide/release-notes/android-sdk) for more details.\
Make sure you update the other unbundled/dependent MoEngage SDKs or modules(like Push Amp Plus, Push Templates, etc.) to the compatible version.
In case, you are not able to update to `5.x.xx` due to some technical limitations or bandwidth issues we have uploaded the last `4.x.xx` version along with the add-on modules on Maven Central. You can update to the below versions.
| Artifact | Version |
| ---------------------------- | ------- |
| moengage-segment-integration | 4.3.01 |
| addon-inbox | 5.3.1 |
| cards | 1.2.01 |
| hms-pushkit | 1.2.01 |
| push-amp-plus | 2.2.01 |
| rich-notification | 1.2.02 |
# Moving from Manifest to Code based Integration
Source: https://moengage.com/docs/developer-guide/android-sdk/migration/moving-from-manifest-to-code-based-integration
Migrate your MoEngage Android SDK setup from manifest metadata to code-based initialization.
We have deprecated integration on MoEngage SDK via Manifest configuration/tags.\
In the latest integration, you need to initialize the SDK in the `onCreate()` of the application class of your app. Below are the metadata flags you need to remove and equivalent APIs you need to call in the `onCreate()` of your application class.\
If you are already initializing the SDK via code in your Application class you can ignore this.
# Adding App Id
## Then
```xml wrap theme={null}
{/* MANDATORY FIELD: APP ID AS SEEN ON MOENGAGE DASHBOARD APP SETTINGS PAGE */}
```
## Now
```java wrap theme={null}
// this is the instance of the application class and "YOUR_WORKSPACE_ID" is the Workspace ID from the dashboard.
MoEngage moEngage = new MoEngage.Builder(this, "YOUR_WORKSPACE_ID")
.build();
MoEngage.initialiseDefaultInstance(moEngage);
```
# Adding Lifecycle Callbacks
## Then
```java wrap theme={null}
MoEHelper.getInstance(getApplicationContext()).autoIntegrate(this);
```
## Now
Remove the above line from your Application class.
# Adding meta tags to manifest for push notifications
## Then
```xml wrap theme={null}
{/* MANDATORY FIELD: SENDER ID , i.e. THE PROJECT NUMBER AS MENTIONED ON GOOGLE CLOUD CONSOLE PROJECTS PAGE */}
{/* MANDATORY FIELD: THE NOTIFICATION SMALL ICON WHICH WILL BE USED TO SET TO NOTIFICATIONS POSTED */}
{/* MANDATORY FIELD: THE NOTIFICATION LARGE ICON WHICH WILL BE USED TO SET TO NOTIFICATIONS POSTED */}
```
## Now
```java wrap theme={null}
MoEngage moEngage =
new MoEngage.Builder(this, "YOUR_WORKSPACE_ID")
.setSenderId("xxxxxxx") // required only if you are using GCM.
.setNotificationSmallIcon(R.drawable.icon)
.setNotificationLargeIcon(R.drawable.ic_launcher)
.build();
MoEngage.initialiseDefaultInstance(moEngage);
```
# Adding meta tags to Skip GCM Registration
## Then
```xml wrap theme={null}
```
## Now
```java wrap theme={null}
new MoEngage.Builder(this, "YOUR_WORKSPACE_ID")
.setSenderId("xxxxxxx") // required only if you are using GCM.
.setNotificationSmallIcon(R.drawable.icon)
.setNotificationLargeIcon(R.drawable.ic_launcher)
.optOutTokenRegistration()
.build();
MoEngage.initialiseDefaultInstance(moEngage);
```
# Showing Multiple Notifications at one go
## Then
```xml wrap theme={null}
{/* OPTIONAL FIELD: THE NOTIFICATION TYPE WHICH WILL BE USED, SINGLE OR MULTIPLE. DEFAULT BEHAVIOR IS SINGLE */}
```
## Now
```java wrap theme={null}
MoEngage moEngage =
new MoEngage.Builder(this, "YOUR_WORKSPACE_ID")
.setSenderId("xxxxxxx") // required only if you are using GCM.
.setNotificationSmallIcon(R.drawable.icon)
.setNotificationLargeIcon(R.drawable.ic_launcher)
.setNotificationType(R.integer.notification_type_multiple)
.build();
MoEngage.initialiseDefaultInstance(moEngage);
```
# Setting notification Color
## Then
```xml wrap theme={null}
[hex code of color]
```
## Now
```java wrap theme={null}
MoEngage moEngage =
new MoEngage.Builder(this, "YOUR_WORKSPACE_ID")
.setSenderId("xxxxxxx") // required only if you are using GCM.
.setNotificationSmallIcon(R.drawable.icon)
.setNotificationLargeIcon(R.drawable.ic_launcher)
.setNotificationColor(R.color.colorAccent)
.build();
MoEngage.initialiseDefaultInstance(moEngage);
```
# Opt-out BackStack creation for Notification
## Then
```java wrap theme={null}
PushManager.getInstance().optoutBackStackBuilder(true);
```
## Now
```java wrap theme={null}
MoEngage moEngage =
new MoEngage.Builder(this, "YOUR_WORKSPACE_ID")
.optOutBackStackBuilder()
.build();
MoEngage.initialiseDefaultInstance(moEngage);
```
# Opt-out activity tracking
## Then
```xml wrap theme={null}
```
## Now
```java wrap theme={null}
ArrayList trackingOptOut = new ArrayList<>();
trackingOptOut.add(SettingsActivity.class);
MoEngage moEngage =
new MoEngage.Builder(this, "YOUR_WORKSPACE_ID")
.setTrackingOptOut(trackingOptOut)
.build();
MoEngage.initialiseDefaultInstance(moEngage);
```
# Opt-out NavBar
## Then
```java wrap theme={null}
InAppManager.getInstance().optOutNavBar(this,true);
```
## Now
```java wrap theme={null}
MoEngage moEngage =
new MoEngage.Builder(this, "YOUR_WORKSPACE_ID")
.optOutNavBar()
.build();
MoEngage.initialiseDefaultInstance(moEngage);
```
# Opt-out Activity from showing InApp
## Then
```xml wrap theme={null}
```
## Now
```java wrap theme={null}
ArrayList inAppOptOut = new ArrayList<>();
inAppOptOut.add(MainActivity.class);
MoEngage moEngage =
new MoEngage.Builder(this, "YOUR_WORKSPACE_ID")
.setInAppOptOut(inAppOptOut)
.build();
MoEngage.initialiseDefaultInstance(moEngage);
```
# Opt-out of MoEngage extras in Deeplink
## Then
```java wrap theme={null}
PushManager.getInstance().optOutMoEngageExtras(true);
```
## Now
```java wrap theme={null}
MoEngage moEngage =
new MoEngage.Builder(this, "YOUR_WORKSPACE_ID")
.optOutMoEngageExtras()
.build();
MoEngage.initialiseDefaultInstance(moEngage);
```
# Updating to 11.x.xx from 10.x.xx
Source: https://moengage.com/docs/developer-guide/android-sdk/migration/updating-to-11xxx-from-10xxx
Migrate to MoEngage Android SDK 11.x.xx with updated APIs, Java 8 support, and modular geofence setup.
# Behavioral Changes
* Target SDK version bumped to API level 29
* Starting SDK version `11.0.00` Geofence is not included when `moe-android-sdk` is added in the application. Refer to the [Location triggered Push](https://www.moengage.com/docs/developer-guide/android-sdk/push/optional/location-triggered) documentation to integrate and use the Geofence module.
* SDK has been re-packaged to have clear separation for internal files and exposed classes. Any class with the package name `com.moengage.*.internal.*` should not be used. APIs in these classes can be updated/removed without prior notice and should not be used by integrating applications.
* InApp is no longer shown via Activity lifecycle callbacks. To show in-app `showInApp()` should be called in the Activity or Fragment. Refer to the [In-App NATIV](https://www.moengage.com/docs/developer-guide/android-sdk/in-app-messages/in-app-nativ) documentation for more details.
* Source and Target Compatibility Updated to Java 8. Enable Java 8 in your application if not done already.
* In this release, we have updated the hosts used by the SDK. In case you have whitelisted MoEngage endpoints in the network configuration of your application update the endpoints. Refer to the [Network Security Configuration](https://www.moengage.com/docs/developer-guide/android-sdk/sdk-integration/basic-integration/sdk-initialization) to know more.
* SDK no longer tracks location by default.
# Update Removed APIs
In version `11.0.00`, we have removed many of the APIs which were long deprecated. If you are still using the deprecated APIs you have to update to the new APIs. Below is a table mapping the equivalents of the removed APIs. Some of the APIs might not have an alternate API as the functionality might have been handled internally or no longer supported.
| Then | Now |
| ----------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------- |
| PushHandler#handlePushPayload (Context, Bundle) | MoEFireBaseHelper#passPushPayload (Context, Bundle) |
| PushHandler#handlePushPayload (Context, Map) | MoEFireBaseHelper#passPushPayload (Context, Map) |
| MoEFireBaseHelper#setOnNonMoEngage PushReceivedListener (OnNonMoEngagePushReceivedListener) | MoEFireBaseHelper#add EventListener(FirebaseEventListener) |
| PushManager#refreshToken(Context, String) | MoEFireBaseHelper#passPushToken (Context, String) |
| PushManager#setMessageListener(Object) | MoEPushHelper#setMessageListener (PushMessageListener) |
| MoEngageNotificationUtils#isFrom MoEngagePlatform(Context, Bundle) | MoEPushHelper#isFromMoEngagePlatform (Bundle) |
| MoEngageNotificationUtils#isFromMoEngagePlatform (Context, Map) | MoEPushHelper#isFromMoEngagePlatform (Map) |
| PushMessageListener#onCreateNotification(Context, Bundle, ConfigurationProvider) | PushMessageListener#onCreateNotification (Context, NotificationPayload) |
| PushManager#getPushHandler() | NA |
| MoEPushHelper#handlePushPayload (Context, Map) | Use `passPushPayload()` of each push module. |
| MoEPushHelper#handlePushPayload (Context, Bundle) | Use `passPushPayload()` of each push module. |
| MoEngage.Builder#optOutBackgroundSync() | MoEngage.Builder#configureDataSync (DataSyncConfig) |
| MoEngage.Builder.optOutPeriodicFlush() | MoEngage.Builder#configureDataSync (DataSyncConfig) |
| MoEngage.Builder.setFlushInterval(long interval) | MoEngage.Builder#configureDataSync (DataSyncConfig) |
| MoEngage.Builder.optOutLocationTracking() | NA |
| MoEngage.Builder.optOutGeofenceBackgroundSync() | MoEngage.Builder#configureGeofence (GeofenceConfig) |
| MoEngage.Builder.enableBackgroundLocationFetch() | NA |
| MoEngage.Builder.enableLocationServices() | MoEngage.Builder#configureGeofence (GeofenceConfig) |
| MoEngage.Builder.optOutRealTime TriggerBackgroundSync() | MoEngage.Builder#configureRealTimeTrigger (RttConfig) |
| MoEngage.Builder.redirectDataToRegion (DATA\_REGION) | MoEngage.Builder#setDataCenter (DataCenter) |
| MoEngage.Builder#optOutDefaultInAppDisplay() | NA |
| MoEngage.Builder#optOutActivityTracking(List) | MoEngage.Builder#configureTrackingOptOut (TrackingOptOutConfig) |
| MoEngage.Builder#optOutGAIDCollection() | MoEngage.Builder#configureTrackingOptOut (TrackingOptOutConfig) |
| MoEngage.Builder#optOutAndroidIdCollection() | MoEngage.Builder#configureTrackingOptOut (TrackingOptOutConfig) |
| MoEngage.Builder#optOutCarrierNameCollection() | MoEngage.Builder#configureTrackingOptOut (TrackingOptOutConfig) |
| MoEngage.Builder#optOutDeviceAttributeCollection() | MoEngage.Builder#configureTrackingOptOut (TrackingOptOutConfig) |
| MoEngage.Builder.setNotificationType() | MoEngage.Builder#configure NotificationMetaData(NotificationConfig) |
| MoEngage.Builder#setDateFormatForCard (String) | MoEngage.Builder#configureCards (CardConfig) |
| MoEngage.Builder#setEmptyInboxImageForCard (int) | MoEngage.Builder#configureCards (CardConfig) |
| MoEngage.Builder#setPlaceHolderImageForCard (int) | MoEngage.Builder#configureCards (CardConfig) |
| MoEHelper#trackEvent(String) | MoEHelper#trackEvent (String, Properties) |
| MoEHelper#trackEvent(String, JSONObject) | MoEHelper#trackEvent(String, Properties) |
| MoEHelper#trackEvent(String, Map) | MoEHelper#trackEvent(String, Properties) |
| MoEHelper#setUserAttribute(String, String, String) | MoEHelper#setUserAttribute(String, Date) |
| MoEHelper#setGender(String) | MoEHelper#setGender(UserGender) |
| MoEHelper#fetchDeviceTriggersIfRequired() | MoERttHelper#syncTriggers(Context) |
| MoEHelper#registerAppBackgroundListener (OnAppBackgroundListener) | MoECallbacks#addAppBackgroundListener (OnAppBackgroundListener) |
| MoEHelper#unregisterAppBackgroundListener() | MoECallbacks#removeAppBackgroundListener (OnAppBackgroundListener) |
| MoEHelper#setOnLogoutCompleteListener (OnLogoutCompleteListener) | MoECallbacks#addLogoutCompleteListener (OnLogoutCompleteListener) |
| MoEHelper#removeLogoutCompleteListener() | MoECallbacks#removeLogoutListener (OnLogoutCompleteListener) |
# APIs Deprecated in 11.0.00
In version `11.0.00` we have deprecated a few APIs to improve performance and better support in the future. Below is the list of deprecated APIs and their alternatives, please update to the latest methods.
| Then | Now |
| --------------------------------------------------------------- | --------------------------------------------------------------------- |
| MoEngage.Builder #setNotificationLargeIcon(int) | MoEngage.Builder#configureNotificationMetaData (NotificationConfig) |
| MoEngage.Builder#setNotificationSmallIcon(int) | MoEngage.Builder#configureNotificationMetaData (NotificationConfig) |
| MoEngage.Builder#setNotificationColor(int) | MoEngage.Builder#configureNotificationMetaData (NotificationConfig) |
| MoEngage.Builder#setNotificationTone(String) | MoEngage.Builder#configureNotificationMetaData (NotificationConfig) |
| MoEngage.Builder#enableMultiple NotificationInDrawer(boolean) | MoEngage.Builder#configureNotificationMetaData (NotificationConfig) |
| MoEngage.Builder#optOutBackStackBuilder() | MoEngage.Builder#configureNotificationMetaData (NotificationConfig) |
| MoEngage.Builder#optOutNotificationLargeIcon() | MoEngage.Builder#configureNotificationMetaData (NotificationConfig) |
| MoEngage.Builder#enableLogsForSignedBuild() | MoEngage.Builder#configureLogs (LogConfig) |
| MoEngage.Builder#enableLogs(int) | MoEngage.Builder#configureLogs (LogConfig) |
| MoEngage.Builder#enablePushKit TokenRegistration() | MoEngage.Builder#configurePushKit (PushKitConfig) |
| MoEngage.Builder#enableSegmentIntegration() | MoEngage.Builder#enablePartnerIntegration (IntegrationPartner) |
| MoEHelper#setUniqueId(double) | MoEHelper#setUniqueId(String) |
| MoEHelper#setUniqueId(float) | MoEHelper#setUniqueId(String) |
| MoEHelper#setUniqueId(int) | MoEHelper#setUniqueId(String) |
| MoEHelper#setUniqueId(long) | MoEHelper#setUniqueId(String) |
| MoEHelper#setAlias(double) | MoEHelper#setAlias(String) |
| MoEHelper#setAlias(float) | MoEHelper#setAlias(String) |
| MoEHelper#setAlias(int) | MoEHelper#setAlias(String) |
# Updating to 12.x.xx from 11.x.xx
Source: https://moengage.com/docs/developer-guide/android-sdk/migration/updating-to-12xxx-from-11xxx
Migrate to MoEngage Android SDK 12.x.xx with updated build configs and modularized InApp and Push.
# Behavioral Changes
* Build Configuration updated to the following
* compileSdk 30
* minSdk 21
* targetSdk 30
* Starting from SDK version 12.0.00 following features are not integrated by default when adding moe-android-sdk as a dependency. If you are using these features refer to their respective integration document and make update the dependencies.
* [InApp](https://www.moengage.com/docs/developer-guide/android-sdk/in-app-messages/in-app-nativ)
* [Push Amplification](https://www.moengage.com/docs/developer-guide/android-sdk/push/optional/push-amplification)
* [Device Trigger](https://www.moengage.com/docs/developer-guide/android-sdk/push/optional/device-triggered)
* APIs to initialize the SDK now throws [*IllegalStateException*](https://developer.android.com/reference/java/lang/IllegalStateException) in case App-id is null or empty.
## External Library Version updates
* androidx.core:core 1.3.1 --> 1.6.0
* androidx.appcompat:appcompat 1.2.0 --> 1.3.1
* androidx.lifecycle:lifecycle-process 2.2.0 --> 2.4.0
* com.google.firebase:firebase-messaging 22.0.0 --> 23.0.0
* Kotlin Standard Library 1.4.20 --> 1.6.0
# Update Removed initialization APIs
| Then | Now |
| --------------------------------------------------------- | --------------------------------------------------------------------- |
| MoEngage.Builder#optOutInAppOnActivity() | MoEngage.Builder#configureInApps (InAppConfig) |
| MoEngage.Builder#optOutNavBar() | MoEngage.Builder#configureInApps (InAppConfig) |
| MoEngage.Builder#optOutBackStackBuilder() | MoEngage.Builder#configureNotificationMetaData (NotificationConfig) |
| MoEngage.Builder#enableMultipleNotificationInDrawer() | MoEngage.Builder#configureNotificationMetaData (NotificationConfig) |
| MoEngage.Builder#setNotificationTone() | MoEngage.Builder#configureNotificationMetaData (NotificationConfig) |
| MoEngage.Builder#setNotificationColor() | MoEngage.Builder#configureNotificationMetaData (NotificationConfig) |
| MoEngage.Builder#setNotificationSmallIcon() | MoEngage.Builder#configureNotificationMetaData (NotificationConfig) |
| MoEngage.Builder#setNotificationLargeIcon() | MoEngage.Builder#configureNotificationMetaData (NotificationConfig) |
| MoEngage.Builder#optOutNotificationLargeIcon() | MoEngage.Builder#configureNotificationMetaData (NotificationConfig) |
| MoEngage.Builder#optOutTokenRegistration() | MoEngage.Builder#configureFcm (FcmConfig) |
| MoEngage.Builder#setSenderId() | Support removed for sender id |
| MoEngage.Builder#configureMiPush(String, String, Boolean) | MoEngage.Builder#configureMiPush (MiPushConfig) |
| MoEngage.Builder#enablePushKitTokenRegistration() | MoEngage.Builder#configurePushKit (PushKitConfig) |
| MoEngage.Builder#enableLogsForSignedBuild() | MoEngage.Builder#configureLogs (LogConfig) |
| MoEngage.Builder#enableLogs() | MoEngage.Builder#configureLogs (LogConfig) |
| MoEngage.Builder#enableSegmentIntegration() | MoEngage.Builder#enablePartnerIntegration (IntegrationPartner) |
# Update removed methods in the core module
| Then | Now |
| ------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| MoECallbacks#addLogoutCompleteListener (OnLogoutCompleteListener) | MoECoreHelper#addLogoutCompleteListener (OnLogoutCompleteListener) |
| MoECallbacks#removeLogoutListener (OnLogoutCompleteListener) | MoECoreHelper#removeLogoutListener (OnLogoutCompleteListener) |
| MoECallbacks#addAppBackgroundListener (AppBackgroundListener) | MoECoreHelper#addAppBackgroundListener (AppBackgroundListener) |
| MoECallbacks#removeAppBackgroundListener (AppBackgroundListener) | MoECoreHelper#removeAppBackgroundListener (AppBackgroundListener) |
| MoEHelper#registerActivityLifecycle() | Support Removed |
| MoEHelper#unregisterLifecycleCallbacks() | Support Removed |
| MoEHelper#registerProcessLifecycleObserver() | Support Removed |
| MoEHelper#unRegisterProcessLifecycleObserver() | Support Removed |
| MoEHelper#trackEvent (String, PayloadBuilder) | MoEAnalyticsHelper#trackEvent (String, Properties) |
| MoEHelper#syncInteractionDataNow() | MoECoreHelper#syncInteractionData() |
| MoEHelper#setAppContext (List) | MoEInAppHelper#setInAppContext (Set) |
| MoEngage#enableSdk(Context) | enableSdk(Context) or MoESdkStateHelper#enableSdk(Context) |
| MoEngage#disableSdk(Context) | disableSdk(Context) or MoESdkStateHelper#disableSdk(Context) |
| MoEngage#optOutDataTracking(Context, boolean) | enableDataTracking(Context)/disableDataTracking(Context) or MoESdkStateHelper#enableDataTracking(Context)/ MoESdkStateHelper#disableDataTracking(Context) |
| MoEngage#optOutPushNotification(Context, boolean) | Support Removed |
| MoEngage#optOutInAppNotification(Context, boolean) | Support Removed |
| MoEngage#initialise(MoEngage, Boolean) | MoEngage#initialiseDefaultIstance (MoEngage, SdkState) |
| MoEngage#initialise(MoEngage) | MoEngage#initialiseDefaultIstance (MoEngage) |
# Changes in Push
* Support for Notification tone dropped
* [*NotificationPayload*](https://moengage.github.io/android-api-reference/pushbase/com.moengage.pushbase.model/-notification-payload/index.html)restructured for easier use.
* Methods removed or replaced
| Then | Now |
| -------------------------------------------------------- | ------------------------------------------------------------- |
| PushMessageListener#onHandleRedirection() | PushMessageListener#onNotificationClick() |
| MoEPushHelper#setMessageListener (PushMessageListener) | MoEPushHelper#registerMessageListener (PushMessageListener) |
## Change in Firebase Messaging
*FirebaseEventListener* removed, instead use the following callbacks
* [*TokenAvailableListener*](https://moengage.github.io/android-api-reference/pushbase/com.moengage.pushbase.listener/-token-available-listener/index.html) for getting the push token whenever it is generated.
* [*NonMoEngagePushListener*](https://moengage.github.io/android-api-reference/moe-push-firebase/com.moengage.firebase.listener/-non-mo-engage-push-listener/index.html) for getting the callback whenever a push is received which is not from MoEngage Server.
* Following methods removed are removed *MoEFireHelper*
* setEventListener(FirebaseEventListener)
* addEventListener(FirebaseEventListener)
Instead, use the methods provided for adding TokenAvailableListener and NonMoEngagePushListener.
## Change in HMS Push Kit
*PushKitEventListener* removed, instead use the following callbacks
* [*TokenAvailableListener*](https://moengage.github.io/android-api-reference/pushbase/com.moengage.pushbase.listener/-token-available-listener/index.html) for getting the push token whenever it is generated.
* [*NonMoEngagePushListener*](https://moengage.github.io/android-api-reference/hms-pushkit/com.moengage.hms.pushkit.listener/-non-mo-engage-push-listener/index.html) for getting the callback whenever a push is received which is not from MoEngage Server.
* Following methods removed are removed *MoEPushKitHelper*
* setEventListener(PushKitEventListener)
* addEventListener(PushKitEventListener)
Refer to the [Configuring HMS Push Kit](https://www.moengage.com/docs/developer-guide/android-sdk/push/optional/push-amp-plus/configuring-hms-push-kit) documentation to know more about how to set the listener.
# Changes in InApp
InAppMessageListener removed instead use the following callbacks
* *OnClickActionListener* for in-app click callback.
* [*SelfHandledAvailableListener*](https://moengage.github.io/android-api-reference/inapp/com.moengage.inapp.listeners/-self-handled-available-listener/index.html) for callbacks when Self handled campaigns are available.
* [*InAppLifeCycleListener*](https://moengage.github.io/android-api-reference/inapp/com.moengage.inapp.listeners/-in-app-life-cycle-listener/index.html) for in-app lifecycle callbacks like shown, dismiss.
Refer to the [InApp](https://www.moengage.com/docs/developer-guide/android-sdk/in-app-messages/in-app-nativ) documentation to know more on how to set the listener.
Methods removed from *MoEInAppHelper*
| Then | Now |
| ------------------------------------------- | ------------------------------------------------------------------------- |
| MoEInAppHelper#getSelfHandledInApp(Context) | MoEInAppHelper#getSelfHandledInApp(Context, SelfHandledAvailableListener) |
# Changes in Inbox
Below are method signature changes
| Then | Now |
| --------------------------------------------------------- | -------------------------------------------------------------- |
| OnMessagesAvailableListener#onMessagesAvailable (List) | OnMessagesAvailableListener#onMessagesAvailable (InboxData) |
| UnClickedCountListener#onCountAvailable (Long) | UnClickedCountListener#onCountAvailable (UnClickedCountData) |
| MoEInboxHelper#fetchMessagesByTag() returns List | MoEInboxHelper#fetchMessagesByTag() returns InboxData |
| MoEInboxHelper#fetchAllMessages() returns List | MoEInboxHelper#fetchAllMessages() returns InboxData |
| MoEInboxHelper#getUnClickedMessagesCount() returns Long | MoEInboxHelper#fetchAllMessages() returns UnClickedCountData |
| OnMessageClickListener#onMessageClick (InboxMessage) | OnMessageClickListener#onMessageClick (MessageClickData) |
# Android 12
Source: https://moengage.com/docs/developer-guide/android-sdk/os-updates/android-12
Review Android 12 behavior changes that affect push notifications and deep links in MoEngage campaigns.
Android 12 is a major version update of the Android Operating System, a yearly release cycle. The major release changes are detailed in the [Android Official Release Notes](https://developer.android.com/about/versions/12/release-notes).
The following are the major behavior changes impacting the MoEngage Platform or MoEngage Android SDK.
The changes are divided into two parts. First, that affects all the applications and the second that affect only the applications targeting Android 12.
# Behavior changes: all apps
The following behavior changes apply to all apps running on Android 12, regardless of **targetSdkVersion**.
## Web intent resolution
Android 12 (API level 31) and later, a generic web intent resolves to an activity in your app, only if your app is approved for the specific domain contained in that web intent. If your app is not approved for the domain, the web intent resolves to the user's default browser app instead.
Apps get approval by doing one of the following:
* Verify the domain using the [Android App Links](https://developer.android.com/about/versions/12/web-intent-resolution#android-app-links).
* Manually [associate your app with the domain](https://developer.android.com/about/versions/12/web-intent-resolution#request-user-associate-app-with-domain) in system settings by the user.
For more information, refer to [Android Web Intent documentation](https://developer.android.com/about/versions/12/web-intent-resolution).
## How does it affect you?
If you are using **http(s)** deep links in your campaigns (Push/InApp/Cards) and the links are not configured as [App Links](https://developer.android.com/training/app-links) in your application, users would be navigated to the browser on devices running Android 12.
## How to fix it?
MoEngage recommends that you configure App Links in your application. Alternatively, edit the campaigns to update the links, if you already have app schema deep-links (example **example://gizmos**) configured in your application for the same activities.
# Behavior changes: Apps targeting Android 12
The following behavior changes apply exclusively to apps that are targeting Android 12 or later, that is **targetSdkVersion** is set to API level 31 or above.
## Pending intents mutability
If your app targets Android 12, specify the mutability of each [PendingIntent](https://developer.android.com/reference/android/app/PendingIntent) object that your app creates. The additional requirement improves your app security.
For more information, refer to [Android release notes](https://developer.android.com/about/versions/12/behavior-changes-12#pending-intent-mutability).
## How does it affect you?
If your application **targetSdkVersion** is 31 and not updated to the latest version of MoEngage SDK (**11.4.00** or later), push notifications would not be displayed on Android 12 devices.
## How to fix it?
Update to the latest MoEngage SDK (**11.4.00** or later) along with the update of **targetSdkVersion** to API level 31.
## Custom notifications
Android 12 changes the appearance and behavior of fully [custom notifications](https://developer.android.com/training/notify-user/custom-notification). Previous versions of custom notifications used the entire notification area and provide layouts and styles. This resulted in anti-patterns confusing users or cause layout compatibility issues on different devices.
For apps targeting Android 12, notifications with custom content views will no longer use the full notification area instead, the system defined standard template is applied.
## How does it affect you?
If your application **targetSdkVersion** is 31 and the **rich-notification** version is either **2.2.01** or previous versions, templates are displayed as truncated or broken on Android 12 devices.
## How to fix it?
Update the rich-notification version to the latest version(2.3.00 or later). MoEngage has temporarily disabled templates when the application **targetSdkVersion** is 31 and the device is running Android 12.
MoEngage support for templates with the new specifications for Android 12 will be available in future releases.
# Changes in backup and restore
Backup and restore functionality is changed for apps that run on and target Android 12 (API level 31). Android backup and restore have two forms:
* **Cloud backups:** User data is stored in the Google Drive of the user for restoring data on that device or a new device.
* **Device-to-device (D2D) transfers:** User data is sent directly to the new device of the user from the older device of the user by using a cable.
For more information on how data is backed up and restored, refer to [Back up user data with Auto Backup](https://developer.android.com/guide/topics/data/autobackup) and [Back up key-value pairs with Android Backup Service](https://developer.android.com/guide/topics/data/keyvaluebackup).
## D2D transfer functionality changes
For apps running on and targeting Android 12 and higher:
* Specifying **android:allowBackup:"false"** does disable backups to Google Drive, but doesn’t disable D2D transfers for the app.
* [Specifying include and exclude rules](https://developer.android.com/guide/topics/data/autobackup#IncludingFiles) with the XML configuration mechanism no longer affects D2D transfers, though it still affects Google Drive backups. To specify rules for D2D transfers, you must use the new configuration format.
# New include and exclude format
Apps running on and targeting Android 12 and later use a different XML configuration format. The new format differentiates between Google Drive backup and D2D transfer by specifying include and exclude rules separately for cloud backups and for D2D transfer.
Optionally, you can also use the new format to specify rules for backup, in which case the old configuration is ignored.
```XML XML wrap theme={null}
...
...
...
...
...
...
```
## How does it affect you?
If your application **targetSdkVersion** is 31, the backup includes MoEngage SDK identifiers backed up and restored after re-install. The restoration of the identifier results in your data being corrupted and the user not being reachable using push notifications.
## How to fix it?
Ensure to add the configuration in the updated format. For more information, refer to [Exclude MoEngage Storage File from Auto Backup](https://www.moengage.com/docs/developer-guide/android-sdk/sdk-integration/basic-integration/exclude-mo-engage-storage-file-from-auto-backup).
While updating the SDK version, please check the compatibility of additional modules and update them accordingly.
# SDK Performance
Source: https://moengage.com/docs/developer-guide/android-sdk/performance/sdk-performance
Review MoEngage Android SDK performance benchmarks for initialization time, memory, and battery impact.
Keeping the SDK performant is of paramount importance to us and we constantly try to improve the performance with each release. Performance includes the time taken to initialize the SDK, within how many milliseconds a method returns, the memory footprint of the SDK when the application is running, battery drainage due to background work, and so on.
# SDK Initialization time
We recommend initializing the SDK on the Main Thread in the [*onCreate()*](https://developer.android.com/reference/android/app/Application#onCreate\(\)) of your Application class, hence it is important for us to keep the initialization simple and fast to ensure it does not affect the application start-up time as start-up time is important for a good user experience.\
The initialization is approximately around 1-5 milliseconds on average. We have tested this on various OEMs and the results seem to be consistent.\
Refer to the below table for more details:
| Device | Device Details | Time (in milliseconds) |
| -------------- | --------------------- | ---------------------- |
| Google Pixel 2 | Android 11 4 GB RAM | 0.878593 |
| Samsung A51 | Android 10 6 GB RAM | 1.95223 |
| One Plus 7 | Android 10 6 GB RAM | 0.365365 |
| Mi Y9 | Android 9 4 GB RAM | 2.658177 |
| Vivo Y51A | Android 7 3 GB RAM | 1.667916 |
| Pixel 4a | Android 12 6 GB RAM | 0.446823 |
| Tab A7 | Andriod 11 3 GB RAM | 0.889791 |
| Mi K20 Pro | Android 10 6 GB RAM | 0.553854 |
## Summary
Across the devices benchmarked above, SDK initialization stays well under 3 ms — small enough that you can safely initialize the SDK synchronously in `Application.onCreate()` without measurable impact on app start-up time.
## Next Steps
* See [SDK Size Impact](/docs/developer-guide/android-sdk/performance/sdk-size-impact) for per-module APK and AAR size data.
* See [SDK Initialization](/docs/developer-guide/android-sdk/sdk-integration/basic-integration/sdk-initialization) for the recommended initialization pattern.
# SDK Size Impact
Source: https://moengage.com/docs/developer-guide/android-sdk/performance/sdk-size-impact
Review the APK and App Bundle size impact of each MoEngage Android SDK artifact.
SDK size is one of the important considerations for us as the inclusion of SDKs can significantly influence the final APK / App Bundle size, affecting the user experience and download time. So, we always aim to keep the impact as low as possible on the final APK / App Bundle size.
# MoEngage SDKs
Refer to the below table for details about the impact on APK / App Bundle size due to different MoEngage artifacts.
The sizes below were measured against MoEngage Version Catalog **3.2.2** (an internal artifact-set version, not an SDK release). Newer BOM releases will shift these numbers; refer to this page as a baseline rather than an exact reference, and contact your account manager for the most current sizing if it influences a release decision.
| Artifact Id | Version | Size Impact (Approx. Kilobytes) |
| -------------------------------------------------------------- | -------- | ------------------------------- |
| cards-core | 1.6.0 | 33 KB |
| cards-ui | 1.6.1 | 277 KB |
| encrypted-storage | 1.3.0 | 1 KB |
| geofence | 3.4.0 | 27 KB |
| hms-pushkit | 4.7.0 | 129 KB |
| inapp | 7.1.1 | 148 KB |
| inbox-core | 2.6.0 | 7 KB |
| inbox-ui | 2.6.0 | 171 KB |
| integration-verifier | 4.4.0 | 62 KB |
| moe-android-sdk | 12.10.02 | 227 KB |
| moengage-segment-kotlin-destination (includes moe-android-sdk) | 1.5.0 | 227 KB |
| push-amp | 4.6.0 | 6 KB |
| push-amp-plus | 6.6.0 | 7 KB |
| realtime-trigger | 2.6.0 | 26 KB |
| rich-notification | 4.7.2 | 155 KB |
| security | 2.6.0 | 2 KB |
# External SDKs
There are some external dependencies that the Application must include while using the MoEngage SDK. Refer to the below table to get the impact due to the external dependencies.
| Dependency | Version | Size Impact (Approx. Kilobytes) |
| ------------------ | --------- | ------------------------------- |
| Firebase Messaging | 23.1.2 | 158 KB |
| HMS Push SDK | 6.3.0.304 | 443 KB |
| MI Push SDK | 5.1.1 | 222 KB |
Some artifact versions in the table above (for example, `moe-android-sdk 12.10.02`) lag the current BOM-recommended versions. The size measurements remain a reasonable per-module baseline, but expect a few KB delta on newer releases.
# Personalize SDK
Source: https://moengage.com/docs/developer-guide/android-sdk/personalize/personalize-sdk
Fetch and track personalized experience and offering campaigns natively from your Android app using the MoEngage Personalize SDK.
# Overview
The MoEngage Android SDK provides a secure framework for delivering personalized campaigns. It simplifies integration by handling user identity and authentication internally, eliminating the need to manage API secrets or manual HTTPS calls. This allows you to fetch and track personalized campaign info directly through a streamlined, native interface.
**Prerequisites**
Before you can fetch personalized experiences, ensure you have completed the following:
* Ensure you have integrated the BOM into your Android SDK. For more information, refer to [Install using BOM](https://www.moengage.com/docs/developer-guide/android-sdk/sdk-integration/basic-integration/Install-Using-BOM).
* Initialization: Call the SDK `initialize()` method within your application entry point ( `onCreate()` on Android. For more information on initialization, refer to [SDK Initialization](https://www.moengage.com/docs/developer-guide/android-sdk/sdk-integration/basic-integration/sdk-initialization).
# Integration MoEngage Personalization
After you configure the BOM, add the required artifact in your `app/build.gradle`. Version numbers are managed by the BOM.
```groovy build.gradle wrap theme={null}
dependencies {
...
implementation("com.moengage:personalization-core")
}
```
```kotlin build.gradle.kts wrap theme={null}
dependencies {
implementation("com.moengage:personalization-core")
}
```
# Implementation Workflow
The `MoEPersonalizeHelper` SDK object is designed to simplify the retrieval and interaction with dynamic, personalized content. Below is a breakdown of the code divided by its primary responsibilities.
## 1. Fetch Meta Experience
Ensure you invoke the metadata call prior to fetching a specific payload or experience. You can use this call to fetch all experience keys under the provided status, providing the context necessary to properly configure subsequent requests.
```kotlin Kotlin wrap theme={null}
MoEPersonalizeHelper.fetchExperiencesMeta(
context = context,
status = listOf(ExperienceStatus.ACTIVE),
onSuccess = { metadata ->
println("Success: $metadata")
},
onFailure = { reason, message ->
println("Failure: $reason")
}
)
```
```java Java wrap theme={null}
MoEPersonalizeHelper.INSTANCE.fetchExperiencesMeta(
context,
Collections.singletonList(ExperienceStatus.ACTIVE),
metadata -> {
System.out.println("Success: " + metadata);
},
(reason, message) -> {
System.out.println("Failure: " + reason);
return Unit.INSTANCE;
}
);
```
Ensure you call `fetchExperiencesMeta` first to retrieve the experience key you need.
The `onSuccess` callback returns an `ExperienceCampaignsMetadata` object containing all necessary metadata for campaign execution in the main thread. Conversely, the `onFailure` callback provides a [`RequestFailureReasonCode` ](https://moengage.github.io/android-api-reference/core/com.moengage.core.model/-request-failure-reason-code/index.html) and an optional message to identify the specific reason for the request's failure.
## 2. Fetch Personalized Content
Once the metadata is fetched, you can retrieve the actual personalized payloads. The SDK provides methods to fetch either a single experience or multiple experiences simultaneously.
EXPERIENCE\_KEY is the unique key that is used in the experience campaign while creating the campaign on the MoEngage dashboard. You can fetch these keys using the fetchExperiencesMeta call.
```kotlin Kotlin wrap theme={null}
// Fetch Single Experience Payload
MoEPersonalizeHelper.fetchExperience(
context = context,
// EXPERIENCE_KEY must be one of the keys returned in the fetchExperiencesMeta call.
experienceKey = EXPERIENCE_KEY,
attributes = mapOf("customOptionalAttributeKey" to "customOptionalAttributeValue"),
onSuccess = { campaignsResult ->
println("Success: $campaignsResult")
},
onFailure = { reason, message ->
println("Failure: $reason")
}
)
// Fetch Multiple Experience Payload
MoEPersonalizeHelper.fetchExperiences(
context = context,
// EXPERIENCE_KEY must be one of the keys returned in the fetchExperiencesMeta call.
experienceKeys = setOf("EXPERIENCE_KEYS"),
attributes = mapOf("customOptionalAttributeKey" to "customOptionalAttributeValue"),
onSuccess = { campaignsResult ->
println("Success: $campaignsResult")
},
onFailure = { reason, message ->
println("Failure: $reason")
}
)
```
```java Java wrap theme={null}
// Fetches single personalized experience campaign.
MoEPersonalizeHelper.INSTANCE.fetchExperience(
context,
// EXPERIENCE_KEY must be one of the keys returned in the fetchExperiencesMeta call.
EXPERIENCE_KEY,
attributes,
campaignsResult -> {
System.out.println("Success: " + campaignsResult);
},
(reason, message) -> {
System.out.println("Failure: " + reason);
return Unit.INSTANCE;
}
);
// Fetches multiple personalized experience campaigns.
Set hashSet = new HashSet<>();
hashSet.add("KEY1");
hashSet.add("KEY2");
MoEPersonalizeHelper.INSTANCE.fetchExperiences(
context,
hashSet,
attributes,
campaignsResult -> {
System.out.println("Success: " + campaignsResult);
},
(reason, message) -> {
System.out.println("Failure: " + reason);
return Unit.INSTANCE;
}
);
```
The `onSuccess` callback returns an `ExperienceCampaignsResult` object containing all necessary metadata for campaign execution in the main thread. Conversely, the `onFailure` callback provides a [`RequestFailureReasonCode` ](https://moengage.github.io/android-api-reference/core/com.moengage.core.model/-request-failure-reason-code/index.html) and an optional message to identify the specific reason for the request's failure.
You can fetch :
* Single Experiences: Retrieve a specific payload using a single `experienceKey`.
* Bulk Experiences: Retrieve multiple payloads at once by passing a Set\ of `experienceKeys`.
Use Cases:
**Contextual Targeting**: You can pass a map of attributes (e.g., current\_page: "home", cart\_value: "500") during the fetch call. This allows MoEngage SDK to serve real-time, state-dependent content (such as a "Free Shipping" banner when the cart value is high enough).
## 3. Track Impressions
After fetching and rendering the personalized content on the UI, it is essential to track user interactions to measure campaign performance.
To measure performance, you must track when an experience is shown. This requires a `campaignEntity`. You can use these methods to log "Impressions" (`experienceShown / experiencesShown`) when the UI renders the content.
### 3a. Track Impressions for Experience Campaigns
```kotlin Kotlin wrap theme={null}
// campaignEntity are placeholders for the campaign objects returned by the fetchExperiences function. They contain the metadata and payload required by the SDK to attribute impressions and clicks to the correct campaign.
MoEPersonalizeHelper.experiencesShown(context, listOf(campaignEntity))
MoEPersonalizeHelper.experienceShown(context, campaignEntity)
```
```java Java wrap theme={null}
// campaignEntity are placeholders for the campaign objects returned by the fetchExperiences
// function. They contain the metadata and payload required by the SDK to attribute impressions and clicks to the correct campaign.
MoEPersonalizeHelper.INSTANCE.experiencesShown(context, Arrays.asList(campaignEntity));
MoEPersonalizeHelper.INSTANCE.experienceShown(context, campaignEntity);
```
### 3b. Track Impressions for Offering Campaigns
[Offerings](/docs/user-guide/decisioning/offer-decisioning/create-offerings) are specific, personalized items (such as a product recommendation or a coupon) contained within a campaign.
```kotlin Kotlin wrap theme={null}
// offeringPayload is the specific data object extracted from an campaignEntity using the unique offering key assigned during campaign creation. It enables tracking at the offering campaign, which is essential when an experience contains multiple offerings within a single payload.
MoEPersonalizeHelper.offeringShown(context, offeringPayload)
MoEPersonalizeHelper.offeringsShown(
context,
listOf(offeringPayload1, offeringPayload2)
)
```
```java Java wrap theme={null}
// offeringPayload is the specific data object extracted from an campaignEntity using the
// unique offering key assigned during campaign creation. It enables tracking at the offering campaign, which is essential when an experience contains multiple offerings within a single payload..
MoEPersonalizeHelper.INSTANCE.offeringShown(context, offeringPayload);
MoEPersonalizeHelper.INSTANCE.offeringsShown(
context,
Arrays.asList(offeringPayload1, offeringPayload2)
);
```
## 4. Track Clicks
### 4a. Track Clicks for Experience Campaigns
You can use these methods to log "Clicks" (`experienceClicked`) when the user interacts with the rendered UI element.
```kotlin Kotlin wrap theme={null}
// campaignEntity are placeholders for the campaign objects returned by the fetchExperiences function. They contain the metadata and payload required by the SDK to attribute impressions and clicks to the correct campaign.
MoEPersonalizeHelper.experienceClicked(context, campaignEntity)
```
```java Java wrap theme={null}
// campaignEntity are placeholders for the campaign objects returned by the fetchExperiences
// function. They contain the metadata and payload required by the SDK to attribute impressions and clicks to the correct campaign.
MoEPersonalizeHelper.INSTANCE.experienceClicked(context, campaignEntity);
```
### 4b. Track Clicks for Offering Campaigns
[Offerings](/docs/user-guide/decisioning/offer-decisioning/create-offerings) are specific, personalized items (such as a product recommendation or a coupon) contained within a campaign.
```kotlin Kotlin wrap theme={null}
// offeringPayload is the placeholder for the specific "Offering" payload extracted from inside a campaignEntity. You can use these objects when you want to track interactions at the individual item level rather than the whole campaign.
// campaignEntity are placeholders for the campaign objects returned by the fetchExperiences function. They contain the metadata and payload required by the SDK to attribute impressions and clicks to the correct campaign.
MoEPersonalizeHelper.offeringClicked(
context,
campaignEntity,
offeringPayload
)
```
```java Java wrap theme={null}
// offeringPayload is the placeholder for the specific "Offering" payload extracted from inside a campaignEntity. You can use these objects when you want to track interactions at the individual item level rather than the whole campaign.
// campaignEntity are placeholders for the campaign objects returned by the fetchExperiences function. They contain the metadata and payload required by the SDK to attribute impressions and clicks to the correct campaign.
MoEPersonalizeHelper.INSTANCE.offeringClicked(
context,
campaignEntity,
offeringPayload
);
```
You can use these Offering-specific functions only if the data is part of an offering payload. For all other experience data, use the standard experience shown/clicked functions.
For more information, refer to the [API documentation](https://moengage.github.io/android-api-reference/personalization-core/com.moengage.campaigns.personalize/-mo-e-personalize-helper/index.html).
# FAQs
No. The SDK returns raw JSON payloads. You are responsible for parsing the data and building the UI components (e.g., banners or carousels).
No. The SDK does not download or cache media assets. Use a standard media loading library to handle images, videos, or fonts referenced in the JSON.
Yes. You can fetch up to 25 experiences in a single call. If you exceed this, the SDK returns the 25 most recently updated experiences and notifies you of the unfulfilled keys.
The SDK returns an empty payload along with a standardized error code (e.g., NETWORK\_ERROR). For more information on all the errors, refer [here](https://moengage.github.io/android-api-reference/personalization-core/com.moengage.campaigns.personalize.model/-experience-failure-reason/index.html).
# Callbacks and Customisation
Source: https://moengage.com/docs/developer-guide/android-sdk/push/advanced/callbacks-and-customisation
Customize push notification display and behavior in your Android app using the MoEngage PushMessageListener.
**Advanced Customization**
Support for advanced use cases where the application wants to customize or alter the default behavior of the MoEngage SDK.
MoEngage SDK allows the client application to optionally customize the notification display and extend/customize the behavior of the notification. Some of the possible customizations are deciding whether to show a notification or not, and tweaking the *NotificationCompat.Builder* object, etc. To do so you need to extend a class called [PushMessageListener](https://moengage.github.io/android-api-reference/pushbase/com.moengage.pushbase.push/-push-message-listener/index.html) provided by the MoEngage SDK and pass on the instance of this class to the MoEngage SDK. Once you have done this you can override the default implementation as per the requirement. Let's look at the above steps in more detail.
# Extending PushMessageListener
The first thing required to customize the notification is creating a class that extends [*PushMessageListener*](https://moengage.github.io/android-api-reference/pushbase/com.moengage.pushbase.push/-push-message-listener/index.html). We will call this class **CustomPushMessageListener** (only for illustration purposes you can have any class name you want). The barebones of this class would look something like below.
```kotlin Kotlin wrap theme={null}
class CustomPushMessageListener : PushMessageListener() {
}
```
```java Java theme={null}
public class CustomPushMessageListener extends PushMessageListener {
}
```
# Passing the instance of CustomPushMessageListener to MoEngage SDK
Pass the instance of `CustomPushMessageListener` to the MoEngage SDK in the `onCreate()` of your `Application` class using [`MoEPushHelper.getInstance().registerMessageListener()`](https://moengage.github.io/android-api-reference/pushbase/com.moengage.pushbase/-mo-e-push-helper/register-message-listener.html).
Register the listener in `Application.onCreate()`, not in an Activity. Push callbacks fire when your app is in the background or killed; an Activity-level registration misses callbacks because the Activity isn't alive yet.
```kotlin Kotlin wrap theme={null}
MoEPushHelper.getInstance().registerMessageListener(CustomPushMessageListener())
```
```java Java wrap theme={null}
MoEPushHelper.getInstance().registerMessageListener(new CustomPushMessageListener());
```
# Optionally control notification display
To control whether a notification is shown to the user or not, you need to override [isNotificationRequired()](https://moengage.github.io/android-api-reference/pushbase/com.moengage.pushbase.push/-push-message-listener/is-notification-required.html) in the *CustomPushMessageListener* class created above.\
If you intend to show the notification overridden, the implementation should return **true** or **false**.
The structure for implementation is described as follows:\
If this method returns **false,** this notification is discarded by the SDK; that is, the notification will not be displayed on the device, and the impression will not be tracked.
```kotlin Kotlin wrap theme={null}
class CustomPushMessageListener : PushMessageListener() {
// decide whether notification should be shown or not.
override fun isNotificationRequired(context: Context, payload: Bundle): Boolean {
// app's logic to decide whether to show notification or not.
// for illustration purpose reading notification preference from SharedPreferences and
// deciding whether to show notification or not. Logic can vary from application to
// application.
val preferences = context.getSharedPreferences("demoapp", 0)
return preferences.getBoolean("notification_preference", true)
}
}
```
```java Java wrap theme={null}
public class CustomPushMessageListener extends PushMessageListener {
// decide whether a notification should be shown or not.
@Override public boolean isNotificationRequired(Context context, Bundle payload) {
// app's logic to decide whether to show a notification or not.
//For illustration purposes reading notification preference from SharedPreferences and
// deciding whether to show a notification or not. Logic can vary from application to
// application.
SharedPreferences preferences = context.getSharedPreferences("demoapp", 0);
return preferences.getBoolean("notification_preference", true);
}
}
```
For more information, refer to [API Reference](https://moengage.github.io/android-api-reference/pushbase/com.moengage.pushbase.push/-push-message-listener/is-notification-required.html).
# Notification Received Callback
To receive a callback whenever a push is received, override the [onNotificationReceived()](https://moengage.github.io/android-api-reference/pushbase/com.moengage.pushbase.push/-push-message-listener/on-notification-received.html) in the *CustomPushMessageListener* class.
```kotlin Kotlin wrap theme={null}
class CustomPushMessageListener : PushMessageListener() {
override fun onNotificationReceived(context: Context, payload: Bundle) {
super.onNotificationReceived(context, payload)
//callback for push notification received.
}
}
```
```java Java wrap theme={null}
public class CustomPushMessageListener extends PushMessageListener {
@Override public void onNotificationReceived(Context context, Bundle payload) {
super.onNotificationReceived(context, payload);
//callback for push notification received.
}
}
```
# Notification Clicked Callback
To receive a callback whenever a push is clicked, override the [onNotificationClicked()](https://moengage.github.io/android-api-reference/pushbase/com.moengage.pushbase.push/-push-message-listener/on-notification-click.html) in the *CustomPushMessageListener* class created as described in the Notification Received Callback.\
This method doubles as a callback and can be used for handling redirection. If you want to handle redirection on notification, click the method should return true, or else false.
```kotlin Kotlin wrap theme={null}
class CustomPushMessageListener : PushMessageListener() {
override fun onNotificationClick(activity: Activity, payload: Bundle): Boolean {
// If you want to handle redirection on notification, click the method should return true, or else false.
return false
}
}
```
```java Java wrap theme={null}
public class CustomPushMessageListener extends PushMessageListener {
@Override public boolean onNotificationClick(Activity activity, Bundle payload) {
// If you want to handle redirection on notification, click the method should return true, or else false.
return false;
}
}
```
# Notification Cleared Callback
To receive a callback whenever a push is cleared, override the [onNotificationCleared()](https://moengage.github.io/android-api-reference/pushbase/com.moengage.pushbase.push/-push-message-listener/on-notification-cleared.html) in the *CustomPushMessageListener* class created above.
```kotlin Kotlin wrap theme={null}
class CustomPushMessageListener : PushMessageListener() {
override fun onNotificationCleared(context: Context, payload: Bundle) {
super.onNotificationCleared(context, payload)
// callback for notification cleared.
}
}
```
```java Java wrap theme={null}
public class CustomPushMessageListener extends PushMessageListener {
@Override public void onNotificationCleared(Context context, Bundle payload) {
super.onNotificationCleared(context, payload);
// callback for notification cleared.
}
}
```
# Custom Action on Action Button Click
To use a custom action on the Action Button click override the [handleCustomAction()](https://moengage.github.io/android-api-reference/pushbase/com.moengage.pushbase.push/-push-message-listener/handle-custom-action.html) in the *CustomPushMessageListener* class created above.
```kotlin Kotlin wrap theme={null}
class CustomPushMessageListener : PushMessageListener() {
override fun handleCustomAction(context: Context, payload: String) {
super.handleCustomAction(context, payload)
// callback for notification custom action
}
}
```
```java Java wrap theme={null}
public class CustomPushMessageListener extends PushMessageListener {
@Override public void handleCustomAction(Context context, String payload) {
super.handleCustomAction(context, payload);
// callback for notification custom action
}
}
```
# Customize Notification
To further customize the notification object, override the [customizeNotification()](https://moengage.github.io/android-api-reference/pushbase/com.moengage.pushbase.push/-push-message-listener/customize-notification.html). This allows for additional settings to be added or modified, such as vibration patterns/LED colors, etc. It is important to note that the super method should be called before adding any customizations to ensure that the default settings are applied.
```kotlin Kotlin wrap theme={null}
class CustomPushMessageListener : PushMessageListener() {
override fun customizeNotification(notification: Notification, context: Context, payload: Bundle) {
super.customizeNotification(notification, context, payload)
// You can customize the `notification` object here
}
}
```
```java Java wrap theme={null}
public class CustomPushMessageListener extends PushMessageListener {
@Override public void customizeNotification(Notification notification, Context context, Bundle payload) {
super.customizeNotification(notification, context, payload);
// You can customize the `notification` object here
}
}
```
# Customize Notification Builder
The SDK provides the [customizeNotificationBuilder()](https://moengage.github.io/android-api-reference/pushbase/com.moengage.pushbase.push/-push-message-listener/customize-notification-builder.html) callback to customize the notification builder object as needed. This feature allows developers to update various properties of the notification builder, such as the channel ID, the auto-dismiss time, etc. It is important to note that the super method should be called before adding any customizations to ensure that the default settings are applied.
```kotlin Kotlin wrap theme={null}
class CustomPushMessageListener : PushMessageListener() {
override fun customizeNotificationBuilder(notificationBuilder: NotificationCompat.Builder, context: Context, notificationPayload: NotificationPayload) {
super.customizeNotificationBuilder(notificationBuilder, context, notificationPayload)
// You can customize the `notificationBuilder` object here
}
}
```
```java Java wrap theme={null}
public class CustomPushMessageListener extends PushMessageListener {
@Override public void customizeNotificationBuilder(NotificationCompat.Builder notificationBuilder, Context context, NotificationPayload notificationPayload) {
super.customizeNotificationBuilder(notificationBuilder, context, notificationPayload);
// You can customize the `notificationBuilder` object here
}
}
```
# Self-handled Notification Received Callback
When you use the SDK callback approach by registering a `PushMessageListener`, the MoEngage SDK provides a dedicated method for self-handled notifications.
**SDK version**: The self-handled notification check is supported starting from Android SDK version **14.06.00**.
**Automatic Impression Tracking:** When using this callback, the MoEngage SDK **automatically tracks notification impressions**. You do not need to call `logNotificationReceived`. You only need to handle the display and manually track clicks if a custom UI is shown.
To receive this callback, override `onSelfHandledNotificationReceived()` in your custom listener class:
```kotlin Kotlin wrap theme={null}
class CustomPushMessageListener : PushMessageListener() {
override fun onSelfHandledNotificationReceived(context: Context, payload: Bundle) {
// Impression is already logged by the SDK automatically
// 1. Logic to handle background data (e.g., sync, logout)
// 2. Logic to build and show a custom notification UI (if required)
}
}
```
```java Java wrap theme={null}
public class CustomPushMessageListener extends PushMessageListener {
@Override
public void onSelfHandledNotificationReceived(@NonNull Context context, @NonNull Bundle payload) {
// Impression is already logged by the SDK automatically
// 1. Logic to handle background data (e.g., sync, logout)
// 2. Logic to build and show a custom notification UI (if required)
}
}
```
For more information on the payload structure received in the `Bundle`, refer to [Background Update Templates](https://www.moengage.com/docs/user-guide/campaigns-and-channels/mobile-push/create/push-templates#background-update).
# Push Display Handled by Application
Source: https://moengage.com/docs/developer-guide/android-sdk/push/advanced/push-display-handled-by-application
Handle push notification display yourself and track impressions and clicks with the MoEngage Android SDK.
This section is only required for very advanced use cases where the application needs to handle the push display on the client side. We believe that the customization provided in [Advanced Push Configuration](https://www.moengage.com/docs/developer-guide/android-sdk/push/advanced/callbacks-and-customisation) should solve most of your use-cases. Refer to this document only if your use-cases cannot be satisfied by the customizations provided in the [Advanced Push Configuration](https://www.moengage.com/docs/developer-guide/android-sdk/push/advanced/callbacks-and-customisation) section.
If the push notification display is handled by the application we need some help from the application to show Push Campaign statistics namely Impressions and Clicks.
# Tracking Notification Impressions
The application needs to notify the SDK if a push from the MoEngage Platform is received via Firebase Cloud Messaging(FCM). SDK provides a helper API [isFromMoEngagePlatform()](https://moengage.github.io/android-api-reference/pushbase/com.moengage.pushbase/-mo-e-push-helper/is-from-mo-engage-platform.html) to check whether push is received from the MoEngage Platform or not. Use this API to check if the received push is from the MoEngage Platform and call [logNotificationReceived()](https://moengage.github.io/android-api-reference/pushbase/com.moengage.pushbase/-mo-e-push-helper/log-notification-received.html) to track notification impressions.
```kotlin Kotlin wrap theme={null}
if (MoEPushHelper.getInstance().isFromMoEngagePlatform(pushPayload)) {
MoEPushHelper.getInstance().logNotificationReceived(context, pushPayload)
}
```
```java Java wrap theme={null}
if (MoEPushHelper.getInstance().isFromMoEngagePlatform(pushPayload)) {
MoEPushHelper.getInstance().logNotificationReceived(context, pushPayload);
}
```
# Tracking Notification Clicks
After the notification is clicked application needs to notify the SDK that a notification is clicked for the SDK to track notification clicks. For SDK to track notification clicks and user sessions accurately, ensure:
* The Push payload received from FCM is added as extras to the Pending intent
* The [logNotificationClick()](https://moengage.github.io/android-api-reference/pushbase/com.moengage.pushbase/-mo-e-push-helper/log-notification-click.html) API is called from **onCreate()** of your Activity which is inflated on notification click.
```kotlin Kotlin wrap theme={null}
MoEPushHelper.getInstance().logNotificationClick(applicationContext, intent)
```
```java Java wrap theme={null}
MoEPushHelper.getInstance().logNotificationClick(getApplicationContext(), getIntent());
```
# Background Update Template (Manual Approach)
* **SDK version**: The self-handled notification check is supported starting from Android SDK version **14.06.00**.
* **Payload information**: For more information on the payload structure and available keys, refer to [Background Update Template](https://www.moengage.com/docs/user-guide/campaigns-and-channels/mobile-push/create/push-templates#background-update).
If you are using a custom `PushMessageListener`,MoEngage recommends using the SDK Callback approach where impressions are tracked automatically. Refer to the [Callback Customization](https://www.moengage.com/docs/developer-guide/android-sdk/push/advanced/callbacks-and-customisation) section for more details.
Use this approach if you have a custom Firebase Messaging Service and wish to manually intercept the MoEngage payload. When handling manually, **you must manually track notification impressions**.
```kotlin Kotlin wrap theme={null}
override fun onMessageReceived(remoteMessage: RemoteMessage) {
val payload = remoteMessage.data
// Step 1: Check if the push is from MoEngage platform
if (MoEPushHelper.getInstance().isFromMoEngagePlatform(payload)) {
// Step 2: Check if it's a self-handled notification
if (MoEPushHelper.getInstance().isSelfHandledNotification(payload)) {
// Log impression manually for self-handled pushes
MoEPushHelper.getInstance().logNotificationReceived(applicationContext, payload)
// Handle Display of notification or custom logic here
return
} else {
// For non-self-handled, pass to SDK for default handling
MoEFireBaseHelper.getInstance().passPushPayload(applicationContext, payload)
}
}
}
```
```java Java wrap theme={null}
@Override
public void onMessageReceived(RemoteMessage remoteMessage) {
Map payload = remoteMessage.getData();
// Step 1: Check if the push is from MoEngage platform
if (MoEPushHelper.getInstance().isFromMoEngagePlatform(payload)) {
// Step 2: Check if it's a self-handled notification
if (MoEPushHelper.getInstance().isSelfHandledNotification(payload)) {
// Log impression manually for self-handled pushes
MoEPushHelper.getInstance().logNotificationReceived(getApplicationContext(), payload);
// Handle Display of notification or custom logic here
return;
} else {
// For non-self-handled, pass to SDK for default handling
MoEFireBaseHelper.getInstance().passPushPayload(getApplicationContext(), payload);
}
}
}
```
# Callback for push delivered by Push Amp
Push-Amp notification is not delivered via FCM, it is delivered directly via MoEngage. You need to set up a callback for receiving payload for messages/campaigns.
### Steps:
1. Setup a callback for notification received. For more information, refer to the [notification received callback documentation](https://www.moengage.com/docs/developer-guide/android-sdk/push/advanced/callbacks-and-customisation).
2. Mark notification as not required. This step is important, if not implemented correctly end-user might end up with two notifications. For more information, refer to the [notification received callback documentation](https://www.moengage.com/docs/developer-guide/android-sdk/push/advanced/callbacks-and-customisation).
In this case, **isNotificationRequired()** should always return false.
# Handling re-direction for push delivered by Push Amp+
Whenever campaigns are delivered using Push Amp+ due to a technical limitation push display cannot be handled by the application. The application gets a callback only once the user clicks on the notification. For more information about how to register for push redirection callback, refer to the [notification clicked callback documentation](https://www.moengage.com/docs/developer-guide/android-sdk/push/advanced/callbacks-and-customisation).
# Related Documents
[MoEngage Android Push Handling Samples](https://github.com/moengage/Android-Sample/tree/master/example/app/src/main/java/com/moengage/example/push).
# FCM Authentication
Source: https://moengage.com/docs/developer-guide/android-sdk/push/basic/fcm-authentication
Generate and upload an FCM private key to the MoEngage Dashboard for Android push notification delivery.
FCM Authentication is one of the methods to enable sending Push notifications to your app installed on Android devices. The FCM Authentication token is used to authorize server requests to Firebase services. You must generate the authentication token and upload it to the MoEngage Dashboard to send Push notifications to Android users.
You have an app created on the Firebase console.
# Authorize Server Requests to Firebase Services
You can authorize server requests to Firebase services using the Private key. FCM Private key is a service account JSON file that contains the details of the Private key generated for authenticating the service account.
## Steps to Generate a Private Key
Login to the [Firebase console](https://console.firebase.google.com/) with your credentials.
1. Select your project.
2. In the top left pane, click the settings icon beside **Project Overview**.
3. Select **Project settings**.
4. On the **Project Settings** page, navigate to the **Service accounts** tab.
5. Click **Generate new private key**. The language you select doesn't matter because we just download the JSON file. The language you select provides you a sample code if you are implementing the FCM communication. MoEngage already does this for you, so select any language and generate the key.
6. Confirm the same by clicking **Generate Key**. This will generate a JSON file containing the Private key.
7. Download the JSON file and upload it to the MoEngage Dashboard at **Settings** > **Channel** > **Push** > **App Push** > **Android** > **FCM Authentication** > **Private key file (Recommended)**.
8. After the JSON is configured, enable FCM in the [cloud console](https://console.cloud.google.com/apis/library/fcm.googleapis.com).
# Changes in Sending Speed with Private Key Configuration
The Firebase Cloud Messaging API has a default rate limit of 600,000 requests per minute. For more information, refer [here](https://firebase.google.com/support/faq#fcm-depr-rampup). If your account reaches that limit, FCM may drop those notifications with the following error:
| Error | Description |
| ---------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| FCM Message rate exceeded (Quotaexceededforquotametric…) | Sending speed is exceeding the service capacity for your FCM account. Requests beyond serving capacity are rejected to limit the ingress flow rate. This happens because of multiple campaigns being triggered at the same time at high speeds. Consider spacing out campaigns or reducing request limits in the campaign delivery control. |
With the reduction in default FCM API rate limits to 600,000 RPM, we will be changing the maximum allowed throttle speed for Push notifications to 500,000 RPM to ensure a single campaign does not breach the project limit. This change will apply to new and existing campaigns. You may still see drops because of multiple campaigns being triggered together.
To reduce such errors, you can take one of the following approaches depending on your use case:
* **Reduce the campaign sending speed** If you are seeing 'FCM Message rate exceeded' a lot in one-time and periodic campaigns, you might be exceeding the sending capacity beyond your allotted quota. We recommend reducing the request limits in the campaign delivery control to avoid exceeding the quota.\
Because of the default rate limit of 600,000 requests per minute for FCM, we recommend setting it to a lower limit (\~200,000 requests per minute) to accommodate other parallel requests such as event-triggered, business event-triggered, and flow-triggered campaigns.
* **Space out one-time and periodic campaigns**\
If you are seeing 'FCM Message rate exceeded' a lot in one-time and periodic campaigns, you might be scheduling multiple campaigns that are being sent out at the same time, which is increasing your overall sending speed. Therefore, space out one-time and periodic campaigns.
* **Request a rate limit increase from FCM** To check your current limit, go to [Google Cloud console](https://console.cloud.google.com/) > APIs & Services > Firebase Cloud Messaging API > Quotas & System Limits or directly to [FCM Quotas](https://console.cloud.google.com/apis/api/fcm.googleapis.com/quotas) and see the value for Send requests per minute. To request a rate limit increase beyond 600,000 RPM, [contact Firebase Support](https://firebase.google.com/support).
# FAQs
MoEngage SDKs are already compliant with these changes from FCM. You need not update MoEngage SDK when you switch to the Private key authentication mechanism.\
The Server Key or Auth key is a mechanism to authenticate MoEngage to send requests to FCM on behalf of your account. It works at an account level and not at an end-user level or device level.\
When you switch from Server key to Private key, all requests will be transferred to Auth key mechanism with no impact on the end user or device.
Sending messages (including upstream messages) with the FCM XMPP and HTTP legacy APIs was deprecated on June 20, 2023 and **will be removed on June 21, 2024**. Refer to Deprecation of Legacy HTTP API. We strongly recommend the Private key for any request sent to Firebase services as soon as possible.
The FCM Server key mechanism will be deprecated on June 21, 2024. After that, FCM may continue to accept requests from the Server key auth mechanism and not deliver the notifications to the devices or it may start rejecting the API requests altogether. In both cases, **your users will no longer receive notifications through FCM**.
After you save the settings in the MoEngage dashboard, your account will immediately switch to using the new API to send Android push notifications including ongoing campaigns. You will not see any drops in sending push notifications during the transition.
The updated FCM API introduces several improvements, including the adoption of the HTTP v1 protocol and the implementation of short-lived access tokens following the OAuth2 security model, replacing the Server key authentication from the previous version. These tokens enhance security by limiting their usage to approximately one hour, minimising the risk in case they are exposed.
With the Private key Auth mechanism, the Firebase Cloud Messaging API has eliminated the bulk sending mechanism and has a default rate limit of 600,000 requests per minute. For more information, refer [here](https://firebase.google.com/support/faq#fcm-depr-rampup). If your account reaches that limit, FCM may drop those notifications. Given bulk notifications are no longer supported, you may see a reduction in campaign sending speed for bulk campaigns such as one-time and periodic campaigns.\
MoEngage has proactively reduced the maximum allowed throttle speed for Push notifications to 500,000 RPM to ensure a single campaign does not breach the project limit. This change will apply to new and existing campaigns. You may still see drops because of multiple campaigns being triggered together. To learn more, refer to changes in [Sending Speed with Private Key Configuration](https://www.moengage.com/docs/developer-guide/android-sdk/push/basic/fcm-authentication#changes-in-sending-speed-with-private-key-configuration).
When you set the authentication as a Private key, the changes are accepted immediately and will be used for sending subsequent requests.
# Notification Runtime Permissions
Source: https://moengage.com/docs/developer-guide/android-sdk/push/basic/notification-runtime-permissions
Handle Android 13 notification runtime permissions in your app using the MoEngage SDK.
Android 13 (API level 33) and higher supports [runtime permission](https://developer.android.com/guide/topics/permissions/overview#runtime) for sending [non-exempt](https://developer.android.com/develop/ui/views/notifications/notification-permission#exemptions) (including Foreground Services (FGS)) notifications from an app [POST\_NOTIFICATIONS](https://developer.android.com/reference/android/Manifest.permission#POST_NOTIFICATIONS). This change helps users focus on the notifications that are most important to them.
Refer to the [official documentation](https://developer.android.com/develop/ui/views/notifications/notification-permission) for more details.
The below-mentioned APIs are supported starting MoEngage core Android SDK version **12.3.01**
When an application runs on Android 13 and wants to show notifications to the user, it must request the user's notification permission. You have two options: let MoEngage handle permissions for you or handle the notification permission with your code.
* MoEngage handles Notification permission.
* You just have to call a single line of code mentioned on this page.
* You maintain the notification permission logic.
* Notify MoEngage SDK if permission to push notifications is granted.
We recommend you let MoEngage handle push notification permissions.
## Notify SDK of permission result
Once the application requests the user for notification permission notify the SDK of the user response using the below API. *If you are letting MoEngage SDK handle the notification permissions, you should ignore this step.*
```Kotlin Kotlin wrap theme={null}
MoEPushHelper.getInstance().pushPermissionResponse(applicationContext, isGranted)
```
```Java Java wrap theme={null}
MoEPushHelper.getInstance().pushPermissionResponse(applicationContext, isGranted);
```
## Update the Permission request count(optional)
Once the application requests the user for notification permission, update the SDK of the request attempts.
**Why does the SDK require permission attempt count?**
SDK requires the attempt count to accurately track the number of times the permission request was attempted. If the user denies the permission request twice the application/SDK cannot request permission further i.e. if we request permission it would be automatically denied without the user seeing the request dialog. As an SDK we cannot be sure of the number of times the user has denied the permission we try to optimize the calls to request permission based on the attempt count.
```Kotlin Kotlin wrap theme={null}
MoEPushHelper.getInstance().updatePushPermissionRequestCount(applicationContext, requestCount)
```
```Java Java wrap theme={null}
MoEPushHelper.getInstance().updatePushPermissionRequestCount(applicationContext, requestCount);
```
## Setup Notification Channels
If the application has already taken notification permission from the user call the below API to set up Notification Channels for showing push notifications.
```Kotlin Kotlin wrap theme={null}
MoEPushHelper.getInstance().setUpNotificationChannels(context)
```
```Java Java theme={null}
MoEPushHelper.getInstance().setUpNotificationChannels(context);
```
## Let MoEngage SDK handle Notification permission
MoEngage SDK provides helper APIs to show the permission request to the end-user or navigate the user to the settings screen for enabling notifications.
Use the below API to show the permission request dialog to the user.
When using the below API SDK automatically tracks the response, sets up the required notification channel, etc, mentioned above.
```Kotlin Kotlin wrap theme={null}
MoEPushHelper.getInstance().requestPushPermission(activity)
```
```Java Java theme={null}
MoEPushHelper.getInstance().requestPushPermission(activity);
```
## Navigate to Notification Settings (situational/optional)
Use the below API to show navigate the user to the Notification Settings for the application on the device.
*Note*: Below Android 8(API level 26) the user is directed to the Application Info screen of your application.
```Kotlin Kotlin wrap theme={null}
MoEPushHelper.getInstance().navigateToSettings(activity)
```
```Java Java theme={null}
MoEPushHelper.getInstance().navigateToSettings(activity);
```
# Push Configuration
Source: https://moengage.com/docs/developer-guide/android-sdk/push/basic/push-configuration
Configure push notification metadata, icons, and Firebase settings for the MoEngage Android SDK.
# Configuring your MoEngage Account
* Ensure you have configured the [Firebase](https://firebase.google.com/docs/android/setup) application.
* Configure FCM Authorization on the MoEngage Dashboard. For more information, refer to [FCM Authentication](https://www.moengage.com/docs/developer-guide/android-sdk/push/basic/fcm-authentication).
* Ensure you add the keys in both the Test and Live environments.
## Adding metadata for push notification
Metadata regarding the notification is required to show push notifications where the small icon and large icon drawable are mandatory.
For more information about API reference for all the possible options, refer to [NotificationConfig](https://moengage.github.io/android-api-reference/core/com.moengage.core.config/-notification-config/index.html).
Use the [*configureNotificationMetaData()*](https://moengage.github.io/android-api-reference/core/com.moengage.core/-mo-engage/-builder/configure-notification-meta-data.html) to transfer the configuration to the SDK.
```Kotlin Kotlin wrap theme={null}
val moEngage = MoEngage.Builder(
application = this,
appId = "YOUR_WORKSPACE_ID",
dataCenter = DataCenter.DATA_CENTER_X
)
.configureNotificationMetaData(
NotificationConfig(
smallIcon = R.drawable.small_icon,
largeIcon = R.drawable.large_icon
)
)
.build()
MoEngage.initialiseDefaultInstance(moEngage)
```
```Java Java wrap theme={null}
MoEngage moEngage = new MoEngage.Builder(this, "YOUR_WORKSPACE_ID", DataCenter.DATA_CENTER_X)
.configureNotificationMetaData(new NotificationConfig(R.drawable.small_icon, R.drawable.large_icon))
.build();
MoEngage.initialiseDefaultInstance(moEngage);
```
Ensure that the SDK is initialized with the metadata in the *onCreate()* of the Application class for push notifications to work.
**Notification Icon Guidelines**
* The notification small icon should be flat, pictured face on, and must be white on a transparent background.
* If you do not have the large icon configured for the notification, pass -1 to use the system default.
## Notification Small Icon Density, Size
| Density (dp) | Size (px) |
| ------------ | --------- |
| MDPI | 24x24 |
| HDPI | 36x36 |
| XHDPI | 48x48 |
| XXHDPI | 72x72 |
| XXXHDPI | 96x96 |
**Critical**
Please ensure the small icon is set. If the small icon is not set, notifications will not be displayed.
## Stacking Notifications (optional)
By default, Android replaces an old notification with a new one from your application. If you want to show multiple notifications from your app stacked in the notification drawer, you can enable this feature.
To enable stacked notifications, set `isMultipleNotificationInDrawerEnabled` to `true` in the `NotificationConfig` during the SDK initialization.
Refer to the [notification config](https://moengage.github.io/android-api-reference/core/com.moengage.core.config/-notification-config/index.html) for additional options.
```Kotlin Kotlin wrap theme={null}
val moEngage = MoEngage.Builder(
application = this,
appId = "YOUR_WORKSPACE_ID",
dataCenter = DataCenter.DATA_CENTER_X
)
.configureNotificationMetaData(
NotificationConfig(
smallIcon = R.drawable.small_icon,
largeIcon = R.drawable.large_icon,
notificationColor = R.color.notiColor,
isMultipleNotificationInDrawerEnabled = true
)
)
.build()
MoEngage.initialiseDefaultInstance(moEngage)
```
```Java Java wrap theme={null}
MoEngage moEngage = new MoEngage.Builder(this, "YOUR_WORKSPACE_ID", DataCenter.DATA_CENTER_X)
.configureNotificationMetaData(new NotificationConfig(R.drawable.small_icon, R.drawable.large_icon, R.color.notiColor, true))
.build();
MoEngage.initialiseDefaultInstance(moEngage);
```
If you do not need to set a specific color for the notification, pass -1 to use the system default.
# Push token registration and Display
Source: https://moengage.com/docs/developer-guide/android-sdk/push/basic/push-token-registration-and-display
Set up push token registration and notification display with FCM in the MoEngage Android SDK.
## Prerequisites
Before you begin, complete the following setup:
* Set up a Firebase project for your app and add the `google-services.json` configuration file. See the [Firebase Android setup guide](https://firebase.google.com/docs/android/setup) for details.
* Generate FCM credentials and add them to the MoEngage dashboard. See [FCM Authentication](/docs/developer-guide/android-sdk/push/basic/fcm-authentication) and [Push Configuration](/docs/developer-guide/android-sdk/push/basic/push-configuration).
## Overview
You must complete two mandatory steps to get push notifications working.
1. Push token registration - Registration for push and generating push tokens.
2. Push display - Receiving the push payload from Firebase Cloud Messaging (FCM) service and showing the notification on the device.
We recommend you let MoEngage handle both steps as it eases the integration process also MoEngage SDK has a built-in retry mechanism to handle token registration failure due to FCM downtime or network issues.
The SDK also supports cases where your application manages the token registration and receives the notification payload.
To support both use cases, this article is broken down into two major sections:
* **Push token registration and display by MoEngage SDK** - Integration steps required for MoEngage SDK to handle Push token registration and display.
* **Push token registration and payload received by the Application** - Integration steps required for your application to handle Push token registration and receiving notification payload.
First, we will look at the integration steps needed if **MoEngage SDK** handles Push token registration and display. Refer to [this section](#push-display-for-non-moengage-payloads-optional) for the integration steps required if **the app** handles push token registration and receives the notification payload.
# Push token registration and display by MoEngage SDK
**Critical**
* When the SDK handles token registration, use firebase-messaging **23.0.0** or higher.
* At any point, your application manifest file should have only one service with intent filter com.google.firebase.MESSAGING\_EVENT. If there is more than one service, only the first service will receive the callback, and MoEngage SDK might never receive the Push Payload, resulting in poor delivery rates.
Add the following code to the manifest file so that MoEngage SDK will receive the notification:
```xml AndroidManifest.xml wrap theme={null}
```
The following steps are optional; use them only if you have the use cases.
## Token Callback - Access to push token (optional)
You might want to get access to the push token that MoEngage registered on behalf of your application. When MoEngage SDK handles push registration, it optionally provides a callback to the application whenever a new token is registered or refreshed.
To get the token callback, implement the [*TokenAvailableListener*](https://moengage.github.io/android-api-reference/pushbase/com.moengage.pushbase.listener/-token-available-listener/index.html) and register for the callback using [*MoEFireBaseHelper.getInstance().addTokenListener()*](https://moengage.github.io/android-api-reference/moe-push-firebase/com.moengage.firebase/-mo-e-fire-base-helper/add-token-listener.html)\_.
We recommend you add the callbacks in the ***onCreate()*** of the Application class since these callbacks can be triggered even when the application is in the background.
## Push display for Non-MoEngage Payloads (optional)
Your App might want to handle notifications that aren't from MoEngage. If you use the receiver provided by the SDK in your application's manifest file, the SDK provides a callback in case a push payload is received for any other server apart from the MoEngage Platform.
To get a callback, implement the [*NonMoEngagePushListener*](https://moengage.github.io/android-api-reference/moe-push-firebase/com.moengage.firebase.listener/-non-mo-engage-push-listener/index.html) and register for the callback using [*MoEFireBaseHelper.getInstance().addNonMoEngagePushListener*](https://moengage.github.io/android-api-reference/moe-push-firebase/com.moengage.firebase/-mo-e-fire-base-helper/add-non-mo-engage-push-listener.html).
We recommend you add the callbacks in the ***onCreate()*** of the Application class since these callbacks can be triggered even when the application is in the background.
# Push token registration and payload received by the Application
Skip this section if already letting MoEngage SDK handle push token registration and display.
By default, MoEngage SDK attempts to register for a push token. If your application handles push token registration, you must opt out of MoEngage SDK's token registration.
## How to opt-out of MoEngage Push token registration?
To opt out of MoEngage's token registration mechanism, disable token registration using [*configureFCM()*](https://moengage.github.io/android-api-reference/core/com.moengage.core/-mo-engage/-builder/configure-fcm.html) API while configuring FCM in the [*MoEngage.Builder*](https://moengage.github.io/android-api-reference/core/com.moengage.core/-mo-engage/-builder/index.html) as described
```Kotlin Kotlin wrap theme={null}
val moEngage = MoEngage.Builder(
application = this,
appId = "YOUR_WORKSPACE_ID",
dataCenter = DataCenter.DATA_CENTER_X
)
.configureNotificationMetaData(
NotificationConfig(
smallIcon = R.drawable.small_icon,
largeIcon = R.drawable.large_icon,
notificationColor = R.color.notiColor,
isMultipleNotificationInDrawerEnabled = false,
isBuildingBackStackEnabled = false,
isLargeIconDisplayEnabled = true
)
)
.configureFcm(FcmConfig(isRegistrationEnabled = false))
.build()
MoEngage.initialiseDefaultInstance(moEngage)
```
```Java Java theme={null}
MoEngage moEngage = new MoEngage.Builder(this, "YOUR_WORKSPACE_ID", DataCenter.DATA_CENTER_X)
.configureNotificationMetaData(new NotificationConfig(R.drawable.small_icon, R.drawable.large_icon, R.color.notiColor, true, true, true))
.configureFcm(new FcmConfig(false))
.build();
MoEngage.initialiseDefaultInstance(moEngage);
```
## Pass the Push Token To MoEngage SDK
Your application must pass the Push Token received from FCM to the MoEngage SDK for the MoEngage platform to send out push notifications to the device.\
Use the [passPushToken()](https://moengage.github.io/android-api-reference/moe-push-firebase/com.moengage.firebase/-mo-e-fire-base-helper/pass-push-token.html) API to pass the push token to the MoEngage SDK.
```Kotlin Kotlin wrap theme={null}
MoEFireBaseHelper.getInstance().passPushToken(applicationContext,token)
```
```Java Java theme={null}
MoEFireBaseHelper.getInstance().passPushToken(getApplicationContext(), token);
```
Ensure the token is passed to MoEngage SDK whenever the push token is refreshed and updated on the application. Passing the token on the application update is important for migration to the MoEngage Platform.
## Passing the Push payload to the MoEngage SDK
Even though you have your receiver to receive the notification payloads, you must pass messages from MoEngage platform to MoEngage SDK to render push notifications, especially rich push notifications. In that case, pass the payload to MoEngage SDK using the following method.
To pass the push payload to the MoEngage SDK call the [*passPushPayload()*](https://moengage.github.io/android-api-reference/moe-push-firebase/com.moengage.firebase/-mo-e-fire-base-helper/pass-push-payload.html) API from the *onMessageReceived()* in the Firebase receiver.\
Before passing the payload to the MoEngage SDK you should check if the payload is from the MoEngage platform using the [*isFromMoengagePlatfrom()*](https://moengage.github.io/android-api-reference/pushbase/com.moengage.pushbase/-mo-e-push-helper/is-from-mo-engage-platform.html) helper API provided by the SDK.
```Kotlin Kotlin wrap theme={null}
if (MoEPushHelper.getInstance().isFromMoEngagePlatform(remoteMessage.data)){
MoEFireBaseHelper.getInstance().passPushPayload(applicationContext, remoteMessage.data)
}else{
// your app's business logic to show notification
}
```
```Java Java theme={null}
if (MoEPushHelper.getInstance().isFromMoEngagePlatform(remoteMessage.getData())) {
MoEFireBaseHelper.getInstance().passPushPayload(getApplicationContext(), remoteMessage.getData());
}else{
// your app's business logic to show notification
}
```
# Rich Landing
A rich landing page can open a web URL inside the app via a push campaign. Irrespective of who handles the push token registration and display, this step is needed to show rich landing pages using the MoEngage platform.
The XML code mentioned below is included in the new SDK version, but you can still use it to change the parent activity of MoEActivity if needed.
To use a rich landing page, you need to add the below code in the **AndroidManifest.xml**
```XML XML wrap theme={null}
```
| Parameter | Description |
| ------------------------- | ------------------------------------------------------------------- |
| \[ACTIVITY\_NAME] | Replace with the name you want to appear on your rich landing page. |
| \[PARENT\_ACTIVITY\_NAME] | Replace with the parent activity name that you want. |
# Device Triggered
Source: https://moengage.com/docs/developer-guide/android-sdk/push/optional/device-triggered
Install the MoEngage realtime-trigger module to enable device-triggered push notifications on Android.
# Overview
Device-triggered campaigns fire push notifications locally on the device in response to user events tracked through the MoEngage SDK — for example, abandoning a cart, viewing a product, or completing a tutorial. The `realtime-trigger` module evaluates campaign conditions on-device against the events you track and renders the notification without a round trip to the server, reducing latency.
To create and configure the campaigns that drive these triggers, see the dashboard guide on [Device-triggered campaigns](https://www.moengage.com/docs/user-guide/campaigns-and-channels/push/create/device-triggered-push).
# Prerequisites
* Complete the [push token registration and display](/docs/developer-guide/android-sdk/push/basic/push-token-registration-and-display) integration. The realtime-trigger module renders notifications through the same push pipeline.
* Track the events that your device-triggered campaigns depend on. See [Track Events](/docs/developer-guide/android-sdk/data-tracking/basic/track-events).
# SDK Installation
## Installing using BOM
Integration using BOM is the recommended approach; see [Install Using BOM](/docs/developer-guide/android-sdk/sdk-integration/basic-integration/Install-Using-BOM). After the BOM is configured, add the dependency in your `app/build.gradle`:
```groovy build.gradle wrap theme={null}
dependencies {
...
implementation("com.moengage:realtime-trigger")
}
```
Version numbers are not required for this dependency; the BOM manages them.
# How It Works
After the dependency is added, the SDK automatically evaluates and renders device-triggered campaigns as the linked events are tracked — no additional code is needed. Configure individual campaigns from the MoEngage dashboard.
# Heads Up Notification
Source: https://moengage.com/docs/developer-guide/android-sdk/push/optional/heads-up-notification
Configure heads-up push notifications using Android notification channels in the MoEngage SDK.
## **Overview**
**Notification Channel Setup**
Heads-up notifications are supported only through Android notification channels. Please refer to the official documentation for the creation of the [notification channels](https://developer.android.com/develop/ui/views/notifications/channels). On Android devices using API levels under 26, the configuration won't be applied, as notification channels that need this setting weren't available until API level 26.
The Notification channel should have **IMPORTANCE\_HIGH** for heads-up notifications, and the same channel ID should be configured in the MoEngage dashboard.
## Configuration
In MoEngage Android version **13.06.00**, a new flag **isDirectPostingForHeadsUpEnabled** has been added in the **NotificationConfig**. If you are using HeadsUp Notification, we strongly recommend you pass the **isDirectPostingForHeadsUpEnabled** flag as true while initializing SDK. By default, it will be false.
For more information about API reference for all the possible options, refer to [NotificationConfig](https://moengage.github.io/android-api-reference/core/com.moengage.core.config/-notification-config/index.html).
Use the [*configureNotificationMetaData()*](https://moengage.github.io/android-api-reference/core/com.moengage.core/-mo-engage/-builder/configure-notification-meta-data.html) to transfer the configuration to the SDK.
```Kotlin Kotlin wrap theme={null}
val moEngage = MoEngage.Builder(
application = this,
appId = "YOUR_WORKSPACE_ID",
dataCenter = DataCenter.DATA_CENTER_X
)
.configureNotificationMetaData(
NotificationConfig(
smallIcon = R.drawable.small_icon,
largeIcon = R.drawable.large_icon,
isDirectPostingForHeadsUpEnabled = true
)
)
.build()
MoEngage.initialiseDefaultInstance(moEngage)
```
```Java Java theme={null}
MoEngage moEngage = new MoEngage.Builder(this, "YOUR_WORKSPACE_ID", DataCenter.DATA_CENTER_X)
.configureNotificationMetaData(new NotificationConfig(R.drawable.small_icon, R.drawable.large_icon, R.color.notiColor, false, true))
.build();
MoEngage.initialiseDefaultInstance(moEngage);
```
## HeadsUp Notification Behaviour
If **isDirectPostingForHeadsUpEnabled** is true, SDK will use a direct posting mechanism to post Rich content directly if it's a high-priority channel. So the floating heads-up notification view will be shown for 5-8 seconds if it is not interacted with. On Android versions prior to API level 26, the flag will be ignored by default, and it will use the re-posting mechanism by default.
Otherwise, if **isDirectPostingForHeadsUpEnabled** is false, it will use the existing reposting mechanism, where text is posted first, and once media assets are downloaded, rich content is posted. This is to ensure at least some content is shown to the user immediately when the notification is received and to maintain a good impression rate. If this is the case, a heads-up window will get dismissed within few seconds due to reposting.
## Non-Headsup Notification Behaviour
For notifications with channel ID having channel importance other than **IMPORTANCE\_HIGH**, SDK will be using the reposting mechanism by default.
Please refer to the [user guide](https://www.moengage.com/docs/user-guide/campaigns-and-channels/mobile-push/notification-features-and-behavior/android-push-heads-up-notifications) to understand more about the heads-up notification behavior and OEM limitations.
# Location Triggered
Source: https://moengage.com/docs/developer-guide/android-sdk/push/optional/location-triggered
Set up geofence-based location-triggered push notifications in your Android app with the MoEngage SDK.
# Prerequisites
To use location triggered (Geofence) push, your app must request the following:
* ACCESS\_FINE\_LOCATION
* ACCESS\_BACKGROUND\_LOCATION if your app targets Android 10 (API level 29) or later.
For location triggered push to work, ensure your Application has the following enabled: Location permission, Play Services location library, and device's location.
For more information, refer to [Android Request Geofences](https://developer.android.com/training/location/geofencing#RequestGeofences).
# SDK Installation
## Installing using BOM
Integration using BOM is the recommended way of integration; refer to the [Install Using BOM](https://www.moengage.com/docs/developer-guide/android-sdk/sdk-integration/basic-integration/Install-Using-BOM) document. Once you have configured the BOM add the dependency in the *app/build.gradle* file as shown below
```Groovy build.gradle wrap theme={null}
dependencies {
...
implementation("com.moengage:geofence")
}
```
Once the BOM is configured, include the specific MoEngage modules required for the application. \
Note: Version numbers are not required for these dependencies; the BOM automatically manages them.
# Configure Geofence
By default, the geofence feature is not enabled. To enable the feature, call the below API.
```kotlin Kotlin wrap theme={null}
MoEGeofenceHelper.getInstance().startGeofenceMonitoring(context)
```
```java Java theme={null}
MoEGeofenceHelper.getInstance().startGeofenceMonitoring(context);
```
At any time if you want to stop the geofence monitoring or feature, use the below API. This API will remove the existing geofences.
```kotlin Kotlin wrap theme={null}
MoEGeofenceHelper.getInstance().stopGeofenceMonitoring(context)
```
```java Java theme={null}
MoEGeofenceHelper.getInstance().stopGeofenceMonitoring(context);
```
## Callback
The MoEngage SDK can notify your application whenever a geofence is triggered. If the listener returns `true`, your application consumes the trigger and the SDK doesn't process it further. Returning `false` lets the SDK process the trigger as usual, so the listener can be used purely for logging or analytics.
Implement [`OnGeofenceHitListener`](https://moengage.github.io/android-api-reference/geofence/com.moengage.geofence.listener/-on-geofence-hit-listener/index.html) and register it in your Application class `onCreate()` using [`MoEGeofenceHelper.getInstance().addListener()`](https://moengage.github.io/android-api-reference/geofence/com.moengage.geofence/-mo-e-geofence-helper/add-listener.html).
```kotlin Kotlin wrap theme={null}
class MyApplication : Application() {
override fun onCreate() {
super.onCreate()
MoEGeofenceHelper.getInstance().addListener(OnGeofenceHitListener { geofenceData ->
// Log or analytics for the triggered geofence.
// Return true to consume the trigger; false lets the SDK process it.
false
})
}
}
```
```java Java theme={null}
public class MyApplication extends Application {
@Override
public void onCreate() {
super.onCreate();
MoEGeofenceHelper.getInstance().addListener(geofenceData -> {
// Log or analytics for the triggered geofence.
// Return true to consume the trigger; false lets the SDK process it.
return false;
});
}
}
```
# Notification Center
Source: https://moengage.com/docs/developer-guide/android-sdk/push/optional/notification-center
Add a notification center to your Android app to display push notification history using MoEngage SDK.
# Overview
The Notification Center shows your push notification history, allowing you to provide an option for the end-user to scroll back and see what they have missed. MoEngage provides out-of-box inbox support with a fully customizable default UI and also provides an option to build your own Notification Center.
# Using MoEngage's default Notification Center
## SDK Installation
## Installing using BOM
Integration using BOM is the recommended way of integration; refer to the [Install Using BOM](https://www.moengage.com/docs/developer-guide/android-sdk/sdk-integration/basic-integration/Install-Using-BOM) document. Once you have configured the BOM add the dependency in the *app/build.gradle* file as shown below
```Groovy build.gradle wrap theme={null}
dependencies {
...
implementation("com.moengage:inbox-ui")
}
```
Once the BOM is configured, include the specific MoEngage modules required for the application. \
Note: Version numbers are not required for these dependencies; the BOM automatically manages them..
## Adding the default Notification Center to your app
To use the default Notification Center UI you can either launch the [Activity](https://moengage.github.io/android-api-reference/inbox-ui/com.moengage.inbox.ui.view/-inbox-activity/index.html) provided by the SDK or add the [Fragment](https://moengage.github.io/android-api-reference/inbox-ui/com.moengage.inbox.ui.view/-inbox-fragment/index.html) provided by the SDK.
### Use Activity
The activity is already declared in the SDK's manifest file and can be inflated using the below code.
```kotlin Kotlin wrap theme={null}
//launching the default InboxActivity
val intent = Intent(this, InboxActivity::class.java)
startActivity(intent)
```
```java Java theme={null}
Intent intent = new Intent(this, InboxActivity.class);
startActivity(intent);
```
### Use Fragment
You can embed [*InboxFragment*](https://moengage.github.io/android-api-reference/inbox-ui/com.moengage.inbox.ui.view/-inbox-fragment/index.html) provided by the SDK in your app's activity. For information on how to add a Fragment to an Activity, refer to [Google documentation on Fragments](https://developer.android.com/guide/fragments).
# Adding parent Activity to the inbox.
Optionally if you want to define the parent activity for the Inbox then use the below code. Replace ***\[PARENT\_ACTIVITY\_NAME]*** with the name of the parent activity.
```xml AndroidManifest.xml wrap theme={null}
```
# Customizing Default Notification Center
SDK provides a certain set of UI customization options. The [InboxActivity](https://moengage.github.io/android-api-reference/inbox-ui/com.moengage.inbox.ui.view/-inbox-activity/index.html) is already in the SDK's manifest file, if a change in label/theme of the default activity is required the activity should be declared again in your app's manifest file and override the attributes.
## Activity Label customization
The default label for the Inbox Activity is ***Notification Inbox***, which you can override by declaring the ***moe\_inbox\_notification\_title*** in the ***strings.xml*** of your application.
```xml strings.xml wrap theme={null}
[LABEL]
```
### Activity theme
To customize the theme, you can either re-declare the activity in your app's manifest and provide the desired theme, or you can override the SDK defaults as described below.
The default theme applied to ***InboxActivity*** is ***MoEInboxTheme.NoActionBar***. You can declare a theme with the same name in the application's ***style.xml*** or ***themes.xml*** or equivalent file.
Below is the definition of the default theme
```xml styles.xml wrap theme={null}
```
To customize the theme, override any of the color attributes by defining the color attributes with the same name in your application's **colors.xml** file.
```xml color.xml wrap theme={null}
{/* Theme customisation */}
#1C64D0@color/moe_black@color/moe_white@color/moe_black
```
For example, if you want to customize the primary dark color define ***moe\_inbox\_color\_primary\_dark*** in your app's **colors.xml** as shown below.
```xml XML wrap theme={null}
[YOUR_COLOR]
```
Similarly, you can customize
* Toolbar Style
* Message Text Appearance
* Header
* Message
* Timestamp
* Scrollbar style
* Empty Notification Center style
You can override the below SDK's default attributes to customize the theme.
```xml XML wrap theme={null}
{/* Toolbar Text Style */}
{/* Header style */}
{/* Message style */}
{/* List Item Separator style */}
{/* Timestamp Style */}
{/* Empty List Style */}
{/* Scrollbar style */}
```
## InboxAdapter Customization
You can override [*InboxAdapter*](https://moengage.github.io/android-api-reference/inbox-ui/com.moengage.inbox.ui.adapter/-inbox-adapter/index.html) to customize the look and feel of the notification Items.
In case of using a custom [*InboxAdapter*](https://moengage.github.io/android-api-reference/inbox-ui/com.moengage.inbox.ui.adapter/-inbox-adapter/index.html), the app should make sure to invoke [*InboxListAdapater.onItemClicked()*](https://moengage.github.io/android-api-reference/inbox-ui/com.moengage.inbox.ui.adapter/-inbox-list-adapter/on-item-clicked.html) on the message item click for the SDK to handle the notification click action; otherwise, the click event will not be tracked, and the on click action will not be executed.
Once your custom InboxAdapter is ready, set the custom Adapter in [MoEInboxUiHelper](https://moengage.github.io/android-api-reference/inbox-ui/com.moengage.inbox.ui/-mo-e-inbox-ui-helper/index.html) before launching the [InboxActivity](https://moengage.github.io/android-api-reference/inbox-ui/com.moengage.inbox.ui.view/-inbox-activity/index.html).
```kotlin Kotlin wrap theme={null}
MoEInboxUiHelper.getInstance().setInboxAdapter(InboxCustomAdapter())
```
## Advanced customization options
1. Deleting Notification from Notification Center\
You can delete a particular notification from Notification Center by calling [deleteItem(](https://moengage.github.io/android-api-reference/inbox-ui/com.moengage.inbox.ui.adapter/-inbox-list-adapter/delete-item.html)**)**.
```kotlin Kotlin wrap theme={null}
public fun onBind(position: Int, inboxMessage: InboxMessage, inboxListAdapter: InboxListAdapter) {
deleteButton.setOnClickListener{
//Call `deleteItem` method to remove Notification
//with item's position and the `InboxMessage` object.
inboxListAdapter.deleteItem(position, inboxMessage)
}
}
```
```java Java theme={null}
public void onBind(int position, InboxMessage inboxMessage, InboxListAdapter inboxListAdapter) {
deleteButton.setOnClickListener(v -> {
//Call `deleteItem` method to remove Notification
//with item's position and the `InboxMessage` object.
inboxListAdapter.deleteItem(position, inboxMessage);
});
}
```
Now, your Notification will get deleted from Notification Center, and Notification Center will get updated.
2. Custom notification item click handling
In case you are using the default Notification Center or using the [*InboxFragment*](https://moengage.github.io/android-api-reference/inbox-ui/com.moengage.inbox.ui.view/-inbox-fragment/index.html) in your app without overriding [*InboxAdapter*](https://moengage.github.io/android-api-reference/inbox-ui/com.moengage.inbox.ui.adapter/-inbox-adapter/index.html), by default, the SDK will handle the notification click action, but you can opt to handle the click action in your app by setting [OnMessageClickListener](https://moengage.github.io/android-api-reference/inbox-ui/com.moengage.inbox.ui.listener/-on-message-click-listener/index.html).
```kotlin Kotlin wrap theme={null}
//set the Message click listener
MoEInboxUiHelper.getInstance().setOnMessageClickListener(listener)
//Remove the Message click listener
MoEInboxUiHelper.getInstance().setOnMessageClickListener(null)
```
```java Java theme={null}
//set the Message click listener
MoEInboxUiHelper.getInstance().setOnMessageClickListener(listener);
//Remove the Message click listener
MoEInboxUiHelper.getInstance().setOnMessageClickListener(null);
```
# Self Handled Notification Center
MoEngage SDK provides helper APIs to fetch the relevant inbox data to display, track events, delete messages, etc. that can be used to build your Notification Centre.

## SDK Installation
Integration using BOM is the recommended approach. See [Install Using BOM](/docs/developer-guide/android-sdk/sdk-integration/basic-integration/Install-Using-BOM). After the BOM is configured, add the following dependency in your `app/build.gradle`:
```groovy build.gradle wrap theme={null}
dependencies {
...
implementation("com.moengage:inbox-core")
}
```
Version numbers are not required for this dependency; the BOM manages them.
# Build Your Notification Center
The below helper APIs can be used to build your own Notification Center. For more information on available helper APIs refer [here](https://moengage.github.io/android-api-reference/inbox-core/com.moengage.inbox.core/-mo-e-inbox-helper/index.html).
## Get all messages
```kotlin Kotlin wrap theme={null}
// synchronous API should not be called on the main thread
MoEInboxHelper.getInstance().fetchAllMessages(context)
// asynchronous API, where listener is an instance of `OnMessagesAvailableListener`
MoEInboxHelper.getInstance().fetchAllMessagesAsync(applicationContext, listener)
```
```java Java theme={null}
// synchronous API should not be called on the main thread
MoEInboxHelper.getInstance().fetchAllMessages(context);
// asynchronous API, where listener is an instance of `OnMessagesAvailableListener`
MoEInboxHelper.getInstance().fetchAllMessagesAsync(applicationContext, listener);
```
## Get UnClicked Notifications count
```kotlin Kotlin wrap theme={null}
MoEInboxHelper.getInstance().getUnClickedMessagesCount(applicationContext)
```
```java Java theme={null}
MoEInboxHelper.getInstance().getUnClickedMessagesCount(applicationContext)
```
## Track Inbox Notification Clicks
```kotlin Kotlin wrap theme={null}
MoEInboxHelper.getInstance().trackMessageClicked(context, inboxMessage)
```
```java Java theme={null}
MoEInboxHelper.getInstance().trackMessageClicked(context, inboxMessage);
```
## Delete Inbox Message
```kotlin Kotlin wrap theme={null}
MoEInboxHelper.getInstance().deleteMessage(context, inboxMessage)
```
```java Java theme={null}
MoEInboxHelper.getInstance().deleteMessage(context, inboxMessage);
```
## Delete All Inbox Message
This feature requires a minimum catalog version **4.5.0**.
```kotlin Kotlin wrap theme={null}
MoEInboxHelper.getInstance().deleteAllMessages(context)
```
```java Java theme={null}
MoEInboxHelper.getInstance().deleteAllMessages(context);
```
## Check if the notification has a coupon code
```kotlin Kotlin wrap theme={null}
MoEInboxHelper.getInstance().hasCouponCode(inboxMessage)
```
```java Java theme={null}
MoEInboxHelper.getInstance().hasCouponCode(inboxMessage);
```
## Get coupon code
```kotlin Kotlin wrap theme={null}
MoEInboxHelper.getInstance().getCouponCode(inboxMessage);
```
```java Java theme={null}
MoEInboxHelper.getInstance().getCouponCode(inboxMessage);
```
Refer to [API reference](https://moengage.github.io/android-api-reference/inbox-core/com.moengage.inbox.core/-mo-e-inbox-helper/index.html) for more details.
# Configuring HMS Push Kit
Source: https://moengage.com/docs/developer-guide/android-sdk/push/optional/push-amp-plus/configuring-hms-push-kit
Configure Huawei HMS Push Kit in your Android app for MoEngage push notification delivery.
* Ensure that you have configured Push Kit on your application.\
For more information, refer to [HMS Push Kit documentation](https://developer.huawei.com/consumer/en/doc/development/HMSCore-Guides/service-introduction-0000001050040060).
* Ensure that you configure MoEngage SDK for receiving push notifications.
# SDK Installation
## Installing Using BOM
Integration using BOM is the recommended way of integration; refer to the [Install Using BOM](https://www.moengage.com/docs/developer-guide/android-sdk/sdk-integration/basic-integration/Install-Using-BOM) document to configure BOM if not done already. Once you have configured the BOM add the dependency in the ***app/build.gradle*** file as shown below
```Groovy build.gradle wrap theme={null}
dependencies {
...
implementation("com.moengage:hms-pushkit")
}
```
Alternatively, you can add the dependency using Artifact ID as described in [Installation using Artifact ID](https://www.moengage.com/docs/developer-guide/android-sdk/sdk-integration/advanced-or-optional/installing-sdk-using-artifact-id). However, installation using BOM is the recommended approach as installing using Artifact ID may lead to version mismatch if mapped incorrectly.
# Push Token Management
When using MoEngage SDK, you can either register for token and pass it on to the MoEngage SDK or simply let MoEngage SDK register for Push Token.
## Token Registration Handled by Application
If your application is registering for Push Notification, use the below API to pass the Push token to the MoEngage SDK.
```kotlin Kotlin wrap theme={null}
MoEPushKitHelper.getInstance().passPushToken(context, token)
```
```java Java theme={null}
MoEPushKitHelper.getInstance().passPushToken(context, token);
```
## Token Registration Handled by MoEngage
By default, the MoEngage SDK does not register for push tokens. You can enable the token registration using the [configurePushKit()](https://moengage.github.io/android-api-reference/core/com.moengage.core/-mo-engage/-builder/configure-push-kit.html) API in the [MoEngage.Builder](https://moengage.github.io/android-api-reference/core/com.moengage.core/-mo-engage/-builder/index.html) while initializing the SDK
```kotlin Kotlin wrap theme={null}
val moEngage = MoEngage.Builder(
application = this,
appId = "YOUR_WORKSPACE_ID",
dataCenter = DataCenter.DATA_CENTER_X
)
.configurePushKit(PushKitConfig(isRegistrationEnabled = true))
.build()
MoEngage.initialiseDefaultInstance(moEngage)
```
```java Java theme={null}
MoEngage moEngage = new MoEngage.Builder(this, "YOUR_WORKSPACE_ID", DataCenter.DATA_CENTER_X)
.configurePushKit(new PushKitConfig(true))
.build();
MoEngage.initialiseDefaultInstance(moEngage);
```
Add the below Service in your application's Manifest file.
```xml AndroidManifest.xml wrap theme={null}
>
```
When MoEngage SDK handles push registration, it optionally provides a callback to the Application whenever a new token is registered, or the token is refreshed.\
An application can get this callback by implementing the [TokenAvailableListener](https://moengage.github.io/android-api-reference/pushbase/com.moengage.pushbase.listener/-token-available-listener/index.html) interface and registering the listener using [MoEPushKitHelper.getInstance().addTokenListener()](https://moengage.github.io/android-api-reference/hms-pushkit/com.moengage.hms.pushkit/-mo-e-push-kit-helper/add-token-listener.html) API.
To use Push Kit, you need to update the *moe-android-sdk* to 10.3.00 or above.
# Configure Your Account on MoEngage
Now you have set up Huawei Push on MoEngage SDK. Proceed to [configuring Huawei Push on MoEngage dashboard](https://www.moengage.com/docs/developer-guide/android-sdk/push/optional/push-amp-plus/configuring-huawei-push-on-mo-engage).
# Configuring Huawei Push on MoEngage
Source: https://moengage.com/docs/developer-guide/android-sdk/push/optional/push-amp-plus/configuring-huawei-push-on-mo-engage
Set up Huawei Push Kit callback URLs and HTTPS certificates on the MoEngage Dashboard.
# Developer Guide
Please make sure that you have set up Push Kit on your application. To configure, use the following:
* [Introduction to HMS Push Kit](https://developer.huawei.com/consumer/en/doc/development/HMS-Guides/push-introduction).
* [Push Receipt](https://developer.huawei.com/consumer/en/doc/development/HMS-Guides/push-receipt#receiptright).
# MoEngage Callback URL
| New DataCenter | New Callback URL | Old Callback URL |
| -------------- | ---------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------- |
| Data Center 1 | [https://api-01.moengage.com/hmspush/dlr](https://api-01.moengage.com/hmspush/dlr) | [https://smsapi.moengage.com/hmspush/dlr](https://smsapi.moengage.com/hmspush/dlr) |
| Data Center 2 | [https://api-02.moengage.com/hmspush/dlr](https://api-02.moengage.com/hmspush/dlr) | [https://smsapi.moengage.com/hmspush/dlr](https://smsapi.moengage.com/hmspush/dlr) |
| Data Center 3 | [https://api-03.moengage.com/hmspush/dlr](https://api-03.moengage.com/hmspush/dlr) | [https://smsapi.moengage.com/hmspush/dlr](https://smsapi.moengage.com/hmspush/dlr) |
| Data Center 4 | [https://api-04.moengage.com/hmspush/dlr](https://api-04.moengage.com/hmspush/dlr) | NA |
Update the URL in the callback address while enabling the recipient.
## HTTPS Certificate for MoEngage callback URL
* Select data storage other than the China region when enabling callback receipt.
* Use the following script to generate the HTTPS certificate while enabling the recipient. For more information, refer to [Push Receipt](https://developer.huawei.com/consumer/en/doc/development/HMS-Guides/push-receipt#receiptright).
```python HTTPS certificate generation using Python wrap theme={null}
import ssl
print(ssl.get_server_certificate(('dashboard-01.moengage.com', 443)))
```
# Configuring Huawei Push
To configure Huawei on the MoEngage dashboard:
1. Navigate to **Settings > Channels > Push > App Push > Huawei (Push Amp+)**.
2. Select Huawei (Push Amp+) in the Platforms available on the menu at the top.
## App ID
Get the App ID from the [Huawei Developer Console](https://developer.huawei.com/) by navigating to\
**Login > AppGallery Connect > My Apps > Select Your App > Distribute > App Information**
## App Secret
Get the App Secret from the [Huawei Developer Console](https://developer.huawei.com/) by navigating to\
**Login > AppGallery Connect > My Apps > Select Your App > Distribute > App Information**
## Package Name
The package name for an application is the unique identifier through which the play store recognizes the application. You can find the package of your application in the **build.gradle** file of your application module.
If you have different package names for debug build and signed build configure the package names accordingly in the Test/Live Environment.
## Small Icon Path
As an important part of a notification message, a notification icon is used to identify the content and type of the message. To ensure a consistent user experience, *HUAWEI Push Kit notification icons* inherit the styles of native Android notification icons.
In other words, a notification icon on the notification bar is displayed in a solid color and the notification icon on the status bar is displayed in black or white. The status bar and notification bar share the same resources. For more information, refer to the [guideline document](https://developer.huawei.com/consumer/en/doc/development/HMS-Guides/push-icon-spec).
The icon file must be stored in the /res/raw directory of an app. For example, the value **/raw/ic\_launcher** indicates the local icon file **ic\_launcher.xxx** stored in **/res/raw**. Currently, supported file formats include PNG and JPG.
# Push AMP Plus Integration
Source: https://moengage.com/docs/developer-guide/android-sdk/push/optional/push-amp-plus/push-amp-plus-integration
Integrate MoEngage Push Amp+ to improve push notification delivery rates on Android OEM devices.
Almost 25-30% of notifications are not delivered because of OEM-related reasons. With Push Amplification+, you can reach these customers and see an immediate uplift in retention rates.\
So, we focused on solving the problem of delivery rates on OEMs and have partnered with OEMs to solve this problem.
To ensure we do not add additional bloat into your application we have made different SDKs for each OEM. You can integrate the relevant SDK based on the device share your application has.
Refer to the respective OEM-specific services documentation below and integrate the relevant ones in your application.
Integration supported are:
* [Configuring HMS Push Kit](https://www.moengage.com/docs/developer-guide/android-sdk/push/optional/push-amp-plus/configuring-hms-push-kit)
# Steps to Remove Mi SDK Dependency
Source: https://moengage.com/docs/developer-guide/android-sdk/push/optional/push-amp-plus/steps-to-remove-mi-sdk-dependency
Remove the discontinued Xiaomi Mi Push SDK dependency from your MoEngage Android integration.
Due to operational concerns, Xiaomi Corporation has recently notified users about the discontinuation of the Mi Push service beyond Mainland China. You might have already received correspondence regarding this matter.
In this regard, we suggest that our customers remove Mi Push SDK from their apps. The steps to do so are enumerated in this article.
## Step 1: Remove Mi aar
1. Navigate to app --> libs
2. Delete MiPush\_SDK\_Client\*.aar
## Step 2: Remove the Mi receiver from the manifest file
Remove the following code from the manifest file.
```xml Remove Mi receiver from the manifest file wrap theme={null}
```
## Step 3: Remove initialization code
Remove the following initialization code from the application.
```kotlin Code to be removed from the application wrap theme={null}
MiPushHelper.initialiseMiPush([context], [appKey], [appId], [region])
```
## Step 4: Delete the helper files
Delete the following helper files:
1. MiPushHelper.kt
2. MiPushReceiver.kt
## Step 5: Remove MoEngage dependency
Remove the MoEngage dependency from the dependencies block of the build.gradle as described:
1. MoEngage Catalog - If you are using the MoEngage catalog remove the following dependency:
```kotlin MoEngage Catalog wrap theme={null}
implementation(moengage.pushAmpPlus)
```
2. Artifact ID - If you have added the artifact ID, remove the Mi-specific push amp dependency:
```kotlin Artifact ID wrap theme={null}
implementation("com.moengage:moe-push-amp-plus:")
```
The Mi Push amplification module was published as `moe-push-amp-plus`. Match the version you previously had pinned in your `build.gradle` and remove that exact line. Leave the core `com.moengage:push-amp` dependency in place if you still use Push Amplification.
# Push Amplification
Source: https://moengage.com/docs/developer-guide/android-sdk/push/optional/push-amplification
Install the MoEngage push-amp module to improve push notification delivery rates on Android.
# Overview
Push Amplification improves push delivery rates by recovering notifications that didn't reach the device through Firebase Cloud Messaging (FCM), such as those lost to FCM outages, OEM-level throttling, or network issues.
When you need OEM-specific channels (Huawei), see [Push Amp Plus Integration](/docs/developer-guide/android-sdk/push/optional/push-amp-plus/push-amp-plus-integration).
# Prerequisites
* Complete the [push token registration and display](/docs/developer-guide/android-sdk/push/basic/push-token-registration-and-display) integration first. Push Amplification only supplements primary FCM delivery; it does not replace it.
# SDK Installation
## Installing using BOM
Integration using BOM is the recommended approach; see [Install Using BOM](/docs/developer-guide/android-sdk/sdk-integration/basic-integration/Install-Using-BOM). After the BOM is configured, add the dependency in your `app/build.gradle`:
```groovy build.gradle wrap theme={null}
dependencies {
...
implementation("com.moengage:push-amp")
}
```
Version numbers are not required for this dependency; the BOM manages them.
# Push Templates
Source: https://moengage.com/docs/developer-guide/android-sdk/push/optional/push-templates
Add rich push notification templates with timers and progress bars to your Android app using MoEngage.
# SDK Installation
## Installing using BOM
Integration using BOM is the recommended way of integration; refer to the [Install Using BOM](https://www.moengage.com/docs/developer-guide/android-sdk/sdk-integration/basic-integration/Install-Using-BOM) document. Once you have configured the BOM add the dependency in the *app/build.gradle* file as shown below
```Groovy build.gradle wrap theme={null}
dependencies {
...
implementation("com.moengage:rich-notification")
}
```
Version numbers are not required for this dependency; the BOM manages it.
# Timer with Progress Bar
## Schedule Exact Alarm Permission
The SDK uses Alarms to periodically update the progress in the progress bar. Starting Android 12, additional permission is required to use exact alarms. Refer to the [documentation](https://developer.android.com/training/scheduling/alarms#exact) for more details. To support the Timer with Progress Bar template on Android 12 and above devices, add the following permission in your manifest file.
```xml AndroidManifest.xml wrap theme={null}
```
Starting Android 14, the Alarm permission is off by default. So you need to ask for explicit Alarm permission if you want to show Timer notifications apart from adding the previous line in your manifest file. Verify if the permission is already granted and accordingly call the following line to get the Alarm permission from the customer.
```kotlin Kotlin wrap theme={null}
startActivity(Intent(Settings.ACTION_REQUEST_SCHEDULE_EXACT_ALARM))
```
```java Java wrap theme={null}
startActivity(new Intent(Settings.ACTION_REQUEST_SCHEDULE_EXACT_ALARM));
```
If the application does not have the `SCHEDULE_EXACT_ALARM` permission, the Timer with Progress Bar template is not shown. Instead, the SDK falls back to the **backup template** configured for the campaign on the MoEngage dashboard. The backup template is the alternate notification you select when creating a Timer with Progress Bar campaign — it shows the timer but omits the progress bar so the notification can still be delivered.
## Customization
The SDK defines the default color values for the progress bar's background and progress color. These can be customized to suit the application's theme by overriding the values for the below SDK-defined color attributes.
To set the custom color in light and dark mode, override the values in **res/values/colors.xml** and **res/values-night/colors.xml**, respectively.
```xml colors.xml wrap theme={null}
[YOUR_COLOR][YOUR_COLOR]
```
# Android Sample App
Source: https://moengage.com/docs/developer-guide/android-sdk/sample-app/android-sample-app
Explore the MoEngage Android sample app on GitHub as a reference for your SDK integration.
**Sample App**
The [MoEngage Android Sample application](https://github.com/moengage/Android-Sample) offers a useful reference point for integrating MoEngage into your Android app.
## Next Steps
1. [Configure Build Settings](/docs/developer-guide/android-sdk/sdk-integration/basic-integration/configuring-build-settings)
2. [Install Using BOM](/docs/developer-guide/android-sdk/sdk-integration/basic-integration/Install-Using-BOM)
3. [SDK Initialization](/docs/developer-guide/android-sdk/sdk-integration/basic-integration/sdk-initialization)
4. [Release Checklist](/docs/developer-guide/android-sdk/checklist/release-checklist)
# Add-On Security
Source: https://moengage.com/docs/developer-guide/android-sdk/sdk-integration/advanced-or-optional/add-on-security
Encrypt data stored by the MoEngage Android SDK on device using the encrypted storage module.
# Encrypted Storage
By default, all the data stored by the SDK on the device is inside the application sandbox. This prevents other applications from accessing the data(both read and write). Though due to compliance standards or any other use cases, you might want additionally encrypt the data stored on the SDK.
To enable this encryption you need to
* Add the below-listed dependencies in your ***app/build.gradle*** file.
* Call the [configureStorageSecurity()](https://moengage.github.io/android-api-reference/core/com.moengage.core/-mo-engage/-builder/configure-storage-security.html) to enable the encryption on the [MoEngage.Builder](https://moengage.github.io/android-api-reference/core/com.moengage.core/-mo-engage/-builder/index.html) object while initializing the SDK
## SDK Installation
The `com.moengage:security` dependency is shared across Encrypted Storage, Encrypted Network Communication, and Network Request Authorization on this page. If you have already added it for another feature, skip ahead to the configuration step.
### Installing using BOM
Integration using BOM is the recommended approach; see [Install Using BOM](/docs/developer-guide/android-sdk/sdk-integration/basic-integration/Install-Using-BOM) to configure it if not done already. Once you have configured the BOM, add the dependency in the ***app/build.gradle*** file as shown below.
```Groovy build.gradle wrap theme={null}
dependencies {
...
implementation("com.moengage:security")
}
```
Alternatively, you can add the dependency using Artifact ID as described in [Installation using Artifact ID](https://www.moengage.com/docs/developer-guide/android-sdk/sdk-integration/advanced-or-optional/installing-sdk-using-artifact-id). However, installation using BOM is the recommended approach, as installing using Artifact ID may lead to version mismatch if mapped incorrectly.
## Enabling Encryption
You can enable the storage encryption using the [configureStorageSecurity()](https://moengage.github.io/android-api-reference/core/com.moengage.core/-mo-engage/-builder/configure-storage-security.html) API in the [*MoEngage.Builder*](https://moengage.github.io/android-api-reference/core/com.moengage.core/-mo-engage/-builder/index.html) while initializing the SDK.
```kotlin Kotlin wrap theme={null}
import com.moengage.core.DataCenter
import com.moengage.core.MoEngage
import com.moengage.core.config.StorageEncryptionConfig
import com.moengage.core.config.StorageSecurityConfig
val moEngage = MoEngage.Builder(
application = this,
appId = "YOUR_WORKSPACE_ID",
dataCenter = DataCenter.DATA_CENTER_X
)
.configureStorageSecurity(StorageSecurityConfig(StorageEncryptionConfig(isEncryptionEnabled = true)))
.build()
MoEngage.initialiseDefaultInstance(moEngage)
```
```java Java theme={null}
import com.moengage.core.DataCenter;
import com.moengage.core.MoEngage;
import com.moengage.core.config.StorageEncryptionConfig;
import com.moengage.core.config.StorageSecurityConfig;
MoEngage moEngage = new MoEngage.Builder(this, "YOUR_WORKSPACE_ID", DataCenter.DATA_CENTER_X)
.configureStorageSecurity(new StorageSecurityConfig(new StorageEncryptionConfig(true)))
.build();
MoEngage.initialiseDefaultInstance(moEngage);
```
Note:
* If storage encryption is enabled in the initialization without adding the above-mentioned dependencies, SDK wouldn't function i.e. no events/user attributes would be tracked, push notifications would not be shown, etc.
* Once storage encryption is enabled and a build is released to production(Play Store or other equivalent stores), you should not disable encryption. Disabling the encryption after the build is released will result in a new user being created in the MoEngage system when the user updates the application.
# Encrypted Network Communication
By default, we use HTTPS protocol for all requests made from the SDK; HTTPS encrypts the requests by default. MoEngage SDK optionally adds another layer of encryption apart from the encryption done by HTTPS.\
To enable this additional encryption, you need to
* Add the below-listed dependencies in your***app/build.gradle*** file.
* Call the [configureNetworkRequest()](https://moengage.github.io/android-api-reference/core/com.moengage.core/-mo-engage/-builder/configure-network-request.html) on the [MoEngage.Builder](https://moengage.github.io/android-api-reference/core/com.moengage.core/-mo-engage/-builder/index.html) object while initializing the SDK
## SDK Installation
***If you have enabled Storage encryption, you can skip the installation step and jump to the Enabling Encryption step.***
### Installing using BOM
Integration using BOM is the recommended way of integration, refer to the [Install Using BOM](https://www.moengage.com/docs/developer-guide/android-sdk/sdk-integration/basic-integration/Install-Using-BOM) document to configure BOM if not done already. Once you have configured the BOM add the dependency in the ***app/build.gradle*** file as shown below
```Groovy build.gradle wrap theme={null}
dependencies {
...
implementation("com.moengage:security")
}
```
Alternatively, you can add the dependency using Artifact ID as described in [Installation using Artifact ID](https://www.moengage.com/docs/developer-guide/android-sdk/sdk-integration/advanced-or-optional/installing-sdk-using-artifact-id#push-templates). However, installation using BOM is the recommended approach, as installing using Artifact ID may lead to version mismatch if mapped incorrectly.
## Enabling Encryption
You can enable the network encryption using the [configureNetworkRequest()](https://moengage.github.io/android-api-reference/core/com.moengage.core/-mo-engage/-builder/configure-network-request.html) API in the [*MoEngage.Builder*](https://moengage.github.io/android-api-reference/core/com.moengage.core/-mo-engage/-builder/index.html) while initializing the SDK.
### Fetch the keys
The key is available on the dashboard. Go to **Settings -> APIs -> API Keys and** copy the **SDK encryption key** for the Test and Live environments (Note: Test and Live environments have separate keys and only the key of the current selected environment will be accessible).
### Configure Network Request
```kotlin Kotlin wrap theme={null}
import com.moengage.core.DataCenter
import com.moengage.core.MoEngage
import com.moengage.core.config.NetworkDataSecurityConfig
import com.moengage.core.config.NetworkRequestConfig
val moEngage = MoEngage.Builder(
application = this,
appId = "YOUR_WORKSPACE_ID",
dataCenter = DataCenter.DATA_CENTER_X
)
.configureNetworkRequest(
NetworkRequestConfig(
NetworkDataSecurityConfig(
isEncryptionEnabled = true,
testEnvironmentEncryptionKey = "YOUR_TEST_ENVIRONMENT_ENCRYPTION_KEY",
liveEnvironmentEncryptionKey = "YOUR_LIVE_ENVIRONMENT_ENCRYPTION_KEY"
)
)
)
.build()
MoEngage.initialiseDefaultInstance(moEngage)
```
```java Java theme={null}
import com.moengage.core.DataCenter;
import com.moengage.core.MoEngage;
import com.moengage.core.config.NetworkDataSecurityConfig;
import com.moengage.core.config.NetworkRequestConfig;
MoEngage moEngage = new MoEngage.Builder(this, "YOUR_WORKSPACE_ID", DataCenter.DATA_CENTER_X)
.configureNetworkRequest(new NetworkRequestConfig(new NetworkDataSecurityConfig(true, "YOUR_TEST_ENVIRONMENT_ENCRYPTION_KEY", "YOUR_LIVE_ENVIRONMENT_ENCRYPTION_KEY")))
.build();
MoEngage.initialiseDefaultInstance(moEngage);
```
***Note:*** When using encrypted network communication, we strongly recommend you enable Storage encryption as well.
# Network Request Authorization
To enable the network request authorization you need to
* Add the below-listed dependencies in your***app/build.gradle*** file.
* Call the [configureNetworkRequest()](https://moengage.github.io/android-api-reference/core/com.moengage.core/-mo-engage/-builder/configure-network-request.html) to enable the authorization on the [MoEngage.Builder](https://moengage.github.io/android-api-reference/core/com.moengage.core/-mo-engage/-builder/index.html) object while initializing the SDK
## SDK Installation
***If you have enabled Storage encryption or Encrypted Network Communication, you can skip the installation step and jump to the Enabling Authorization step.***
### Installing using BOM
Integration using BOM is the recommended way of integration, refer to the [Install Using BOM](https://www.moengage.com/docs/developer-guide/android-sdk/sdk-integration/basic-integration/Install-Using-BOM) document to configure BOM if not done already. Once you have configured the BOM add the dependency in the ***app/build.gradle*** file as shown below
```Groovy build.gradle wrap theme={null}
dependencies {
...
implementation("com.moengage:security")
}
```
Alternatively, you can add the dependency using Artifact ID as described in [Installation using Artifact ID](https://www.moengage.com/docs/developer-guide/android-sdk/sdk-integration/advanced-or-optional/installing-sdk-using-artifact-id#device-triggered). However, installation using BOM is the recommended approach, as installing using Artifact ID may lead to version mismatch if mapped incorrectly.
## Enabling Authorization
You can enable the network authorization using the [configureNetworkRequest()](https://moengage.github.io/android-api-reference/core/com.moengage.core/-mo-engage/-builder/configure-network-request.html) API in the [*MoEngage.Builder*](https://moengage.github.io/android-api-reference/core/com.moengage.core/-mo-engage/-builder/index.html) while initializing the SDK.
```kotlin Kotlin wrap theme={null}
import com.moengage.core.DataCenter
import com.moengage.core.MoEngage
import com.moengage.core.config.NetworkAuthorizationConfig
import com.moengage.core.config.NetworkRequestConfig
val moEngage = MoEngage.Builder(
application = this,
appId = "YOUR_WORKSPACE_ID",
dataCenter = DataCenter.DATA_CENTER_X
)
.configureNetworkRequest(NetworkRequestConfig(NetworkAuthorizationConfig(isAuthorizationEnabled = true)))
.build()
MoEngage.initialiseDefaultInstance(moEngage)
```
```java Java theme={null}
import com.moengage.core.DataCenter;
import com.moengage.core.MoEngage;
import com.moengage.core.config.NetworkAuthorizationConfig;
import com.moengage.core.config.NetworkRequestConfig;
MoEngage moEngage = new MoEngage.Builder(this, "YOUR_WORKSPACE_ID", DataCenter.DATA_CENTER_X)
.configureNetworkRequest(new NetworkRequestConfig(new NetworkAuthorizationConfig(true)))
.build();
MoEngage.initialiseDefaultInstance(moEngage);
```
Adding the above dependency and enabling the flag isn't enough for this feature to work; there is some additional configuration required on our side to enable this feature completely. In case you want to use this feature, reach out to your account manager or the MoEngage Support team.
# Custom Proxy Domain - Android
Source: https://moengage.com/docs/developer-guide/android-sdk/sdk-integration/advanced-or-optional/custom-proxy-domain-android
Route MoEngage SDK traffic through your own subdomain to bypass ad blockers on Android.
In today's privacy-focused digital landscape, many users employ ad blockers or private DNS services. These tools often block network requests to known third-party domains, including analytics and engagement platforms. When these requests are blocked, you lose critical data visibility, and your users may not receive in-app messages or push notifications.
To ensure reliable campaign delivery, MoEngage offers the **Custom Proxy Domain** feature. This allows you to route MoEngage SDK traffic through a subdomain of your own primary domain (e.g., `data.yourcompany.com`). Because the traffic appears as first-party communication, it bypasses common ad-blocking lists.
## Onboarding Process
Setting up a Custom Proxy Domain requires a one-time DNS delegation process between your team and MoEngage.
Before updating your SDK code, you must complete the DNS delegation setup. For a detailed guide on picking a domain and configuring NS records, refer to [DNS Delegation](https://www.moengage.com/docs/user-guide/getting-started/integration/custom-proxy-sub-domains).
### Step 1: Choose a Subdomain
Select a subdomain that is short and does not contain keywords typically flagged by filters (for example, avoid "tracking", "ads", or "moengage").
### Step 2: Request DNS Delegation
Reach out to your MoEngage Customer Success Manager (CSM) or [raise a support ticket](https://www.moengage.com/docs/user-guide/contact-support/raise-a-support-ticket-from-the-moengage-login-page) to initiate the request. Provide your chosen subdomain.
### Step 3: Configure NS Records
MoEngage will provide you with four Name Server (NS) records. You must add these records to your DNS provider's configuration for the chosen subdomain.
## Implementation
Once the DNS delegation is verified, update your SDK initialization logic. The SDK will dynamically rewrite all MoEngage endpoints (API calls and CDN assets) to use your custom proxy domain.
### Update MoEngage Configuration
```kotlin Kotlin wrap theme={null}
import com.moengage.core.DataCenter
import com.moengage.core.MoEngage
import com.moengage.core.config.DomainConfig
import com.moengage.core.config.NetworkRequestConfig
val moEngage = MoEngage.Builder(application, "YOUR_WORKSPACE_ID", DataCenter.DATA_CENTER_X)
.configureNetworkRequest(
NetworkRequestConfig(DomainConfig("CUSTOM_DOMAIN"))
)
.build()
MoEngage.initialiseDefaultInstance(moEngage)
```
```java Java theme={null}
import com.moengage.core.DataCenter;
import com.moengage.core.MoEngage;
import com.moengage.core.config.DomainConfig;
import com.moengage.core.config.NetworkRequestConfig;
MoEngage moEngage = new MoEngage.Builder(this, "YOUR_WORKSPACE_ID", DataCenter.DATA_CENTER_X)
.configureNetworkRequest(new NetworkRequestConfig(new DomainConfig("CUSTOM_DOMAIN")))
.build();
MoEngage.initialiseDefaultInstance(moEngage);
```
## Best Practices and Validation
1. **Domain Selection:** Keep your subdomain string short (5-8 characters).
2. **Network Logs:** Verify that requests start with your custom subdomain (e.g., `sdk-01.data.example.com`).
3. **Asset Loading:** Ensure images in campaigns load correctly.
# Installing SDK using Artifact Id
Source: https://moengage.com/docs/developer-guide/android-sdk/sdk-integration/advanced-or-optional/installing-sdk-using-artifact-id
Install MoEngage Android SDK modules individually using their Maven Central artifact IDs.
We recommend integrating the MoEngage BOM for easier dependency management. Refer to the [documentation](https://www.moengage.com/docs/developer-guide/android-sdk/sdk-integration/basic-integration/Install-Using-BOM) to know more.
# SDK Versions
| Artifact Name | Artifact Id | Version |
| -------------------------------- | ----------------- | ------- |
| Core | moe-android-sdk | |
| Self-Handled Cards | cards-core | |
| Default Cards | cards-ui | |
| Encrypted Storage | encrypted-storage | |
| Geofence | geofence | |
| HMS Pushkit | hms-pushkit | |
| InApp | inapp | |
| Self-Handled Notification Center | inbox-core | |
| Default Notification Center | inbox-ui | |
| PushAmp | push-amp | |
| PushAmpPlus | push-amp-plus | |
| Device Triggered | realtime-trigger | |
| Push Templates | rich-notification | |
| Security | security | |
# SDK Installation
Add the following dependency in the ***app/build.gradle(.kts)*** file to integrate the required modules. Make sure to replace **\$sdkVersion** with the appropriate SDK version
## Core
```Kotlin build.gradle(.kts) wrap theme={null}
dependencies {
...
implementation("com.moengage:moe-android-sdk:$sdkVersion")
}
```
## Self-Handled Cards
```Kotlin build.gradle(.kts) wrap theme={null}
dependencies {
...
implementation("com.moengage:cards-core:$sdkVersion")
}
```
## MoEngage's Default Cards
```Kotlin build.gradle(.kts) wrap theme={null}
dependencies {
...
implementation("com.moengage:cards-ui:$sdkVersion")
}
```
## Encrypted Storage
```Kotlin build.gradle(.kts) wrap theme={null}
dependencies {
...
implementation("com.moengage:encrypted-storage:$sdkVersion")
}
```
## Geofence
```Kotlin build.gradle(.kts) wrap theme={null}
dependencies {
...
implementation("com.moengage:geofence:$sdkVersion")
}
```
## HMS Pushkit
```Kotlin build.gradle(.kts) wrap theme={null}
dependencies {
...
implementation("com.moengage:hms-pushkit:$sdkVersion")
}
```
## InApp
```Kotlin build.gradle(.kts) wrap theme={null}
dependencies {
...
implementation("com.moengage:inapp:$sdkVersion")
}
```
## Self-Handled Notification Center
```Kotlin build.gradle(.kts) wrap theme={null}
dependencies {
...
implementation("com.moengage:inbox-core:$sdkVersion")
}
```
## MoEngage's default Notification Center
```Kotlin build.gradle(.kts) wrap theme={null}
dependencies {
...
implementation("com.moengage:inbox-ui:$sdkVersion")
}
```
## Push Amplification
```Kotlin build.gradle(.kts) wrap theme={null}
dependencies {
...
implementation("com.moengage:push-amp:$sdkVersion")
}
```
## PushAmpPlus
```Kotlin build.gradle(.kts) wrap theme={null}
dependencies {
...
implementation("com.moengage:push-amp-plus:$sdkVersion")
}
```
## Device Triggered
```Kotlin build.gradle(.kts) wrap theme={null}
dependencies {
...
implementation("com.moengage:realtime-trigger:$sdkVersion")
}
```
## Push Templates
```Kotlin build.gradle(.kts) wrap theme={null}
dependencies {
...
implementation("com.moengage:rich-notification:$sdkVersion")
}
```
## Security
```Kotlin build.gradle(.kts) wrap theme={null}
dependencies {
...
implementation("com.moengage:security:$sdkVersion")
}
```
# JWT Authentication
Source: https://moengage.com/docs/developer-guide/android-sdk/sdk-integration/advanced-or-optional/jwt-authentication
Secure your MoEngage data collection by implementing JWT authentication in your Android app.
## Overview
JWT (JSON Web Token) authentication is a standard method for securely verifying user identity. By implementing JWT authentication, you add a critical layer of security to your data collection process with MoEngage.
The feature ensures that the data sent on behalf of your identified users is authentic and is not tampered with. This security is achieved by requiring a token that is cryptographically signed by your own server, which prevents unauthorized users from impersonating your legitimate users.
Before you begin the implementation, ensure you meet the following requirements:
* Your application must use the MoEngage Android SDK version **14.04.00** or higher to access the JWT authentication feature.
* You must have access to your MoEngage dashboard to manage public keys and configure the feature's enforcement settings.
The following diagram illustrates the interaction between your application, your server, the MoEngage SDK, and the MoEngage server:
## Integration
Perform the following to integrate JWT authentication into your Android application:
### Step 1: Enable JWT Authentication
You can enable JWT authentication during [SDK initialization](https://www.moengage.com/docs/developer-guide/android-sdk/sdk-integration/basic-integration/sdk-initialization) by configuring the ***NetworkAuthorizationConfig*** property on the ***MoEngage.Builder*** object.
```kotlin Kotlin wrap theme={null}
val moEngage = MoEngage.Builder(
application = application,
appId = "YOUR_WORKSPACE_ID",
dataCenter = DataCenter.DATA_CENTER_X
)
.configureNetworkRequest(NetworkRequestConfig(NetworkAuthorizationConfig(isJwtEnabled = true)))
.build()
MoEngage.initialiseDefaultInstance(moEngage)
```
```java Java theme={null}
MoEngage moEngage = new MoEngage.Builder(application, "YOUR_WORKSPACE_ID", DataCenter.DATA_CENTER_X)
.configureNetworkRequest(new NetworkRequestConfig(new NetworkAuthorizationConfig(true)))
.build();
MoEngage.initialiseDefaultInstance(moEngage);
```
### Step 2: Pass the JWT to the SDK
Your application is responsible for managing the JWT lifecycle. The recommended flow is to fetch a token upon user login and pass the token to the SDK. You should also check if the token has expired on subsequent app launches and fetch a new one if necessary.
Use the [***MoECoreHelper.passAuthenticationDetails()***](https://moengage.github.io/android-api-reference/core/com.moengage.core/-mo-e-core-helper/pass-authentication-details.html) to provide the token to the SDK.
```kotlin Kotlin wrap theme={null}
val data = AuthenticationData.Jwt("YOUR_JWT_TOKEN", "USER_IDENTIFIER")
MoECoreHelper.passAuthenticationDetails(context, data)
```
```java Java theme={null}
AuthenticationData data = new AuthenticationData.Jwt("YOUR_JWT_TOKEN", "USER_IDENTIFIER");
MoECoreHelper.INSTANCE.passAuthenticationDetails(application.getApplicationContext(), data);
```
### Step 3: Handle Authentication Errors
To handle token validation errors that the MoEngage server returns, register an [***OnAuthenticationError***](https://moengage.github.io/android-api-reference/core/com.moengage.core.model.authentication/-on-authentication-error/index.html) listener. The SDK invokes this listener when an authentication error occurs, which allows your application to fetch and provide a new token. Register the listener in a global scope, such as the `onCreate()` of your `Application` class, to ensure your application always receives callbacks.
```kotlin Kotlin wrap theme={null}
val authErrorListener = OnAuthenticationError { error ->
when (error.data) {
is ErrorData.Jwt -> {
// Handle JWT authentication error
val errorData = error.data as ErrorData.Jwt
val jwtError = errorData.code
val message = errorData.message
// Take appropriate action based on the jwtError
}
}
}
MoECoreHelper.registerAuthenticationListener(authErrorListener)
```
```java Java theme={null}
OnAuthenticationError errorListener = new OnAuthenticationError() {
@Override
public void onError(@NonNull AuthenticationError error) {
switch (error.getType()) {
case JWT:
ErrorData.Jwt jwtError = (ErrorData.Jwt) error.getData();
// Handle JWT error
JwtError jwtErrorType = jwtError.getCode();
String message = jwtError.getMessage();
// Take action based on jwtErrorType
break;
}
}
};
MoECoreHelper.INSTANCE.registerAuthenticationListener(errorListener);
```
* If an API request fails due to an authentication error, the SDK will not retry the request until your application provides a new token.
* After 10 consecutive authentication failures in a single session, the SDK will stop attempting to sync data until the next session begins. This counter resets after any successful sync.
* Upon user logout, if a data sync fails due to a JWT error, the pending data will be deleted, and no retry will be attempted.
# Network Security Configuration
Source: https://moengage.com/docs/developer-guide/android-sdk/sdk-integration/advanced-or-optional/network-security-configuration
Whitelist MoEngage SDK domains in your custom Android network security configuration.
This section is only required if you have a custom Network Security Configuration in your application.
If your application has a network security configuration that whitelists domains to which your application can send data to whitelist the below domains based on the MoEngage Data Center you have selected while integrating the SDK.
| Data Center | Host |
| --------------- | ------------------- |
| DATA\_CENTER\_1 | sdk-01.moengage.com |
| DATA\_CENTER\_2 | sdk-02.moengage.com |
| DATA\_CENTER\_3 | sdk-03.moengage.com |
| DATA\_CENTER\_4 | sdk-04.moengage.com |
| DATA\_CENTER\_5 | sdk-05.moengage.com |
| DATA\_CENTER\_6 | sdk-06.moengage.com |
By default, the SDK sends the data to *DATA\_CENTER\_1*. In case, you have not configured any data center while initializing the SDK whitelist the host corresponding to *DATA\_CENTER\_1*
# SDK Build Specifications
Source: https://moengage.com/docs/developer-guide/android-sdk/sdk-integration/advanced-or-optional/sdk-configuration
Review the build specifications and library versions used to compile the MoEngage Android SDK.
This page lists the build specifications (target/compile/min SDK, Kotlin, AGP) used to compile the MoEngage Android SDK and the library versions it depends on. For runtime configuration options such as enabling features, push, in-app, or analytics, see the relevant integration pages under **SDK Integration**.
# Build Specifications
| Configuration | API Level |
| ----------------------------- | --------- |
| Target SDK Version | 35 |
| Compile SDK Version | 35 |
| Minimum SDK Version | 23 |
| Kotlin Version | 1.9.23 |
| Android Gradle Plugin Version | 8.13.2 |
Refer to the [API level to code name mapping](https://source.android.com/setup/start/build-numbers) to get the version name.
# Library Dependency
The SDK is compiled using the versions described below, but the application can override these at runtime.
The SDK is compiled with the following libraries:
```groovy Groovy wrap theme={null}
androidx.core:core:1.15.0
androidx.appcompat:appcompat:1.7.0
androidx.lifecycle:lifecycle-process:2.8.7
```
The build configuration is available only for the current SDK version. If you are using an older version of the SDK and want to know the build configuration, contact MoEngage support or contact your Account Manager.
# Install Using BOM
Source: https://moengage.com/docs/developer-guide/android-sdk/sdk-integration/basic-integration/Install-Using-BOM
Use the MoEngage Bill of Materials to manage compatible Android SDK module versions.
Connect your IDE to the [MoEngage docs MCP server](/docs/developer-guide/connect-your-ide-to-moengage-docs) for accurate, context-aware SDK guidance inside your development environment. Your assistant searches this documentation directly, helping you move through the integration with confidence.
**Information**
You can now get notified whenever MoEngage releases a new version of the Android Native SDK. For more information, refer to [Subscribe to MoEngage SDK Releases](/docs/release-notes/sdks/android).
Review the [SDK Deprecation Policy](/docs/developer-guide/sdk-lifecycle-and-policies/sdk-deprecation-policy) for supported SDK versions and deprecation timelines before integrating.
## Overview
Starting from Android SDK core version [14.04.03](/docs/release-notes/sdks/android#core-sdk-14-04-03) and later, MoEngage recommends using the Bill of Materials (BOM) to integrate the Android SDK. By defining a single BOM version, you ensure that all MoEngage modules, such as Push Kit, Rich Media, and Geofence, automatically use compatible versions. BOM simplifies dependency management, prevents version conflicts, and eliminates the need to manually track version numbers for each individual artifact.
## Add the BOM to your application
To integrate the BOM, add the `android-bom` dependency to the application-level `build.gradle` file. The version number is specified only in this dependency.
```kotlin build.gradle.kts (Kotlin) wrap theme={null}
dependencies {
// Import the MoEngage BOM
implementation(platform("com.moengage:android-bom:"))
}
```
Replace the `` with the relevant version number. For more information on BOM versions, refer to the [Android SDK release notes](/docs/release-notes/sdks/android).
## Add MoEngage Modules
Once the BOM is configured, include the specific MoEngage modules required for the application.\
**Note:** Version numbers are not required for these dependencies; the BOM automatically manages them.
```kotlin build.gradle.kts (Kotlin) wrap theme={null}
dependencies {
// -------------------------------------------------
// OPTIONAL MODULES (Add based on required features)
// -------------------------------------------------
// Cards Core - Required if using Cards Core APIs directly
implementation("com.moengage:cards-core")
// Cards UI - Required for Cards (feed) UI
implementation("com.moengage:cards-ui")
// Geofence - Required for Geofence-based campaigns
implementation("com.moengage:geofence")
// HMS Push Kit - Required for Huawei Push Notifications
implementation("com.moengage:hms-pushkit")
// InApp - Required for In-App Messaging
implementation("com.moengage:inapp")
// Inbox Core - Required if using Inbox Core APIs directly
implementation("com.moengage:inbox-core")
// Inbox UI - Required for Notification Center (Inbox) UI
implementation("com.moengage:inbox-ui")
// Push Amp - Required for Push Amplification
implementation("com.moengage:push-amp")
// Real Time Trigger - Required for device-triggered campaigns
implementation("com.moengage:realtime-trigger")
// Rich Notification - Required for Push Templates
implementation("com.moengage:rich-notification")
// Security - Required for Security features, for example; storage encryption
implementation("com.moengage:security")
}
```
**Info**
While MoEngage strongly recommends using the BOM for seamless dependency management, you can opt to manually configure versions for each artifact. If you choose this approach, you must ensure that the versions of all integrated modules are compatible with one another.
To find the latest versions and compatible artifact combinations, please refer to the [Installing SDK using Artifact Id](https://www.moengage.com/docs/developer-guide/android-sdk/sdk-integration/advanced-or-optional/installing-sdk-using-artifact-id).
## Add Androidx Libraries
The SDK depends on a few Androidx libraries for its functioning. Add the below Androidx libraries in your application if not done already.
```groovy Groovy wrap theme={null}
implementation("androidx.core:core:1.15.0")
implementation("androidx.appcompat:appcompat:1.7.1")
implementation("androidx.lifecycle:lifecycle-process:2.8.7")
```
The MoEngage SDK depends on the **lifecycle-process** library for a few key features to work and the latest version of **lifecycle-process** depends on the **androidx.startup:startup-runtime** library. Hence do not remove the **InitializationProvider** component from the manifest. When adding other Initializers using the **startup-runtime** ensure the Initializer for **lifecycle-process** library is also added. Refer to the [documentation](https://developer.android.com/jetpack/androidx/releases/lifecycle#2.4.0) to know how to add the Initializer.
## Impact of using BOM
Using the BOM does not increase the application size. It functions strictly as a version manager to ensure compatibility. Only the specific modules included in the `dependencies` block (e.g., `push-kit`, `android-sdk`) are bundled into the application.
## Why use BOM?
* **Simplified Versioning:** Eliminates the need to manage individual artifact versions. Updating the BOM version updates the entire integration.
* **Compatibility:** The BOM ensures that all integrated modules work efficiently together, preventing runtime crashes caused by mismatched library versions.
* **Cleaner Configuration:** Maintains a clean `build.gradle` file with a single source of truth for the SDK version.
# Configuring Build Settings
Source: https://moengage.com/docs/developer-guide/android-sdk/sdk-integration/basic-integration/configuring-build-settings
Configure your Android project build settings by adding the Maven repository and enabling Java 8.
**Start here.** This is the first step in integrating the MoEngage Android SDK. Once you finish this page, continue with [Install Using BOM](/docs/developer-guide/android-sdk/sdk-integration/basic-integration/Install-Using-BOM).
## Add Maven Repository
Add the `mavenCentral()` repository in your project. Where you declare it depends on your project's repository management approach.
### Projects using `settings.gradle` (Android Studio Flamingo and later)
Newer Android projects (created with Android Studio Flamingo or later) manage repositories in `settings.gradle(.kts)`. Add `mavenCentral()` to `dependencyResolutionManagement`:
```Groovy settings.gradle theme={null}
dependencyResolutionManagement {
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
repositories {
google()
mavenCentral()
}
}
```
```Kotlin settings.gradle.kts theme={null}
dependencyResolutionManagement {
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
repositories {
google()
mavenCentral()
}
}
```
### Projects using `allprojects` (older Android Studio versions)
If your project still uses the project-level `build.gradle` to declare repositories, add `mavenCentral()` there.
```Groovy build.gradle wrap theme={null}
buildscript {
repositories {
mavenCentral()
}
}
allprojects {
repositories {
mavenCentral()
}
}
```
## Enable Java 8
The SDK is target and source compatible with version 8 of the Java Programming Language. Enable Java 8 in the application `build.gradle(.kts)` if not done already.
```Groovy build.gradle wrap theme={null}
android {
...
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = '1.8'
}
}
```
```Kotlin build.gradle.kts theme={null}
android {
...
compileOptions {
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = "1.8"
}
}
```
For more information about samples, refer to the [Android Sample](https://github.com/moengage/Android-Sample).
## Next Step
Continue to [Install Using BOM](/docs/developer-guide/android-sdk/sdk-integration/basic-integration/Install-Using-BOM) to add the MoEngage SDK to your app.
# Data Center
Source: https://moengage.com/docs/developer-guide/android-sdk/sdk-integration/basic-integration/data-center
Configure data center redirection in the MoEngage Android SDK to comply with your data policies.
# Data Redirection
If your app needs to redirect data to a specific zone due to a data regulation policy, please configure the zone in the MoEngage initializer object. Pass the data center as the third argument to [*MoEngage.Builder*](https://moengage.github.io/android-api-reference/core/com.moengage.core/-mo-engage/-builder/index.html).
```kotlin Kotlin wrap theme={null}
// this is the instance of the application class and "YOUR_WORKSPACE_ID" is the APP ID from the dashboard.
val moEngage = MoEngage.Builder(
application = this,
appId = "YOUR_WORKSPACE_ID",
dataCenter = DataCenter.DATA_CENTER_X
)
.build()
MoEngage.initialiseDefaultInstance(moEngage)
```
```java Java theme={null}
// this is the instance of the application class and "YOUR_WORKSPACE_ID" is the APP ID from the dashboard.
MoEngage moEngage = new MoEngage.Builder(this, "YOUR_WORKSPACE_ID", DataCenter.DATA_CENTER_X)
.build();
MoEngage.initialiseDefaultInstance(moEngage);
```
The following details the different data centers and dashboard hosts
| Data Center | Dashboard host |
| --------------- | ------------------------- |
| DATA\_CENTER\_1 | dashboard-01.moengage.com |
| DATA\_CENTER\_2 | dashboard-02.moengage.com |
| DATA\_CENTER\_3 | dashboard-03.moengage.com |
| DATA\_CENTER\_4 | dashboard-04.moengage.com |
| DATA\_CENTER\_5 | dashboard-05.moengage.com |
| DATA\_CENTER\_6 | dashboard-06.moengage.com |
**Important**
The dashboard host URL provides the Data Center information of your account. Ensure that you contact your account manager or [raise a support ticket](https://www.moengage.com/docs/user-guide/contact-support/raise-a-support-ticket-through-moengage-dashboard) to know the data center before you change the data center in the Android SDK.
# Exclude MoEngage Storage File from Auto-Backup
Source: https://moengage.com/docs/developer-guide/android-sdk/sdk-integration/basic-integration/exclude-mo-engage-storage-file-from-auto-backup
Exclude MoEngage storage files from Android auto-backup to prevent data corruption after restore.
Mandatory integration step to prevent data corruption.
Auto backup service of Android periodically backs up the Shared Preference file, Database files, and so on.
For more information, refer to [Auto Backup](https://developer.android.com/guide/topics/data/autobackup).
The backup results in MoEngage SDK identifiers to be backed up and restored after re-install. The restoration of the identifier results in your data being corrupted and the user not being reachable using push notifications.
To ensure data is not corrupted after a backup is restored, opt-out of MoEngage SDK storage files.
# Add a backup descriptor in the *application* tag of the Manifest file.
The backup descriptor should be assigned to *fullBackupContent* attribute of the *application* tag.
```xml AndroidManifest.xml wrap theme={null}
```
# Exclude MoEngage Files in the descriptor file.
Exclude the following database file and shared preference file in the descriptor to ensure these files are ***not*** backed up.
```xml XML wrap theme={null}
```
If you only want to exclude files only from MoEngage SDK instead of creating a new file you can directly add the backup descriptor file provided by the SDK to the manifest file as shown below.
```xml AndroidManifest.xml wrap theme={null}
```
For applications with **targetSdkVersion** 31 or above exclusion has to be done in the new format as well.
# Exclusion for API level 31 or above
## Add manifest flag
Declare the new configuration file in the manifest file as shown below.
```xml XML wrap theme={null}
```
# Exclude MoEngage Files in the configuration file.
You can exclude MoEngage files from the configuration as shown below
```xml XML wrap theme={null}
...
...
...
```
If you only want to exclude files only from MoEngage SDK, instead of creating a new file, you can directly add the backup descriptor file and data extraction rules file provided by the SDK to the manifest file, as shown below.
```xml AndroidManifest.xml wrap theme={null}
```
# SDK Initialization
Source: https://moengage.com/docs/developer-guide/android-sdk/sdk-integration/basic-integration/sdk-initialization
Initialize the MoEngage Android SDK in your Application class using your Workspace ID and data center.
# SDK Configuration
Get the Workspace ID from the Settings Page of the dashboard **Dashboard** --> **Settings** --> **App** --> **General** on the MoEngage dashboard and initialize the MoEngage SDK in the Application class `onCreate()`.
**Updated in SDK version 15.00.00** Starting this version, the SDK throws an `IllegalStateException` if any API is invoked before the SDK is initialized. Ensure `MoEngage.initialiseDefaultInstance()` is called in `Application.onCreate()` before invoking any other SDK APIs. For details, see the [release notes](/docs/release-notes/sdks/android#7th-july-2026).
**Note** Initialize the SDK on the main thread inside `onCreate()` and not create a worker thread and initialize the SDK on that thread.
**Updated in SDK version 15.00.00** The Java `MoEngage.Builder` constructor now accepts a `String` for the data center instead of a `DataCenter` enum object. Update your Java initialization code as shown below. This change does not affect Kotlin. For details, see the [release notes](/docs/release-notes/sdks/android#7th-july-2026).
```kotlin Kotlin wrap theme={null}
import com.moengage.core.DataCenter
import com.moengage.core.MoEngage
val moEngage = MoEngage.Builder(this, "YOUR_WORKSPACE_ID", DataCenter.DATA_CENTER_X)
.build()
//replace X with your data center number
MoEngage.initialiseDefaultInstance(moEngage)
```
```java Java theme={null}
import com.moengage.core.DataCenter;
import com.moengage.core.MoEngage;
MoEngage moEngage = MoEngage.builder(this, "YOUR_WORKSPACE_ID", DataCenter.getDataCenterX())
.build();
MoEngage.initialiseDefaultInstance(moEngage);
```
## Portfolio (Optional)
In your MoEngage account, if your [portfolio](https://www.moengage.com/docs/user-guide/settings/account/portfolio/portfolio) is configured with multiple projects, use the methods as shown below.
```kotlin Kotlin wrap theme={null}
import com.moengage.core.DataCenter
import com.moengage.core.MoEngage
import com.moengage.core.config.ProjectConfig
val moEngage = MoEngage.Builder(this, "YOUR_WORKSPACE_ID", DataCenter.DATA_CENTER_X)
.configureProject(ProjectConfig(""))
.build()
MoEngage.initialiseDefaultInstance(moEngage)
```
```java Java wrap theme={null}
import com.moengage.core.DataCenter;
import com.moengage.core.MoEngage;
import com.moengage.core.config.ProjectConfig;
MoEngage moEngage = MoEngage.builder(this, "YOUR_WORKSPACE_ID", DataCenter.getDataCenterX())
.configureProject(new ProjectConfig(""))
.build();
MoEngage.initialiseDefaultInstance(moEngage);
```
Following details of the different data centers you need to set based on the dashboard hosts:
| Data Center | Dashboard host |
| :------------------------- | :------------------------ |
| `DataCenter.DATA_CENTER_1` | dashboard-01.moengage.com |
| `DataCenter.DATA_CENTER_2` | dashboard-02.moengage.com |
| `DataCenter.DATA_CENTER_3` | dashboard-03.moengage.com |
| `DataCenter.DATA_CENTER_4` | dashboard-04.moengage.com |
| `DataCenter.DATA_CENTER_5` | dashboard-05.moengage.com |
| `DataCenter.DATA_CENTER_6` | dashboard-06.moengage.com |
For more information about the detailed list of possible configurations, refer to the [API reference](https://moengage.github.io/android-api-reference/core/com.moengage.core/-mo-engage/-builder/index.html).
**Critical** All the configurations are added to the builder before initialization. If you are calling initialize at multiple places, ensure that all the required flags and configurations are set each time you initialize to maintain consistency in behavior.
# Data Flow
SDK detects the build type of the installed application and the basis that it decides whether data should be sent to the Test/Live Environment of the MoEngage Platform.
* UnSigned/Debug Build — Data flows to Test environment
* Signed/Live Build — Data flows to Live environment.
# How to fix Token Drop?
Source: https://moengage.com/docs/developer-guide/android-sdk/troubleshooting-and-faqs/how-to-fix-token-drop
Learn why push token drops occur in your Android app and how to fix them for reliable delivery.
# What is a token drop?
Token drop is a situation or case where the MoEngage Platform does not have push tokens for all the users of your application.\
Say your application is installed by 100 new users every day but there are tokens for only 60 users on the MoEngage Platform, this is regarded as a token drop.
# Why is it important to fix it?
To send out a push notification to your end-users push token is required, without a token push cannot be sent. Hence to keep your users engaged it is very important to fix the token drop.
# Why does token drop occur?
Token drop can happen because of various reasons:
* Poor/No Internet connectivity which results in Firebase SDK not generating the token.
* Play services version mismatch, play services version on the device isn't compatible with the Firebase version used in the application
* Application has some internal check/flag based on which they decide whether to pass the token to the MoEngage SDK and during the token generation that flag is disabled hence token isn't passed to MoEngage SDK.
* Integration Error, push token is not being passed to the MoEngage SDK.
# How to fix it?
* If the token is not available on App-Open, deploy a retry mechanism where the application periodically attempts to generate a token till a token is successfully generated.
* If you have any check before passing the token to MoEngage SDK please remove it or keep the flag enabled by default to ensure tokens are passed on the first app open itself.
* Revisit the [Push Notification](/docs/developer-guide/android-sdk/push/basic/push-configuration) to check if the implementation for the passing token is properly done.
# Suggestions for retry Mechanism
We recommend that the application attempts to generate a push token on every app open and pass it to the MoEngage SDK. If the app does not get the token from the Firebase API for any reason, the app should retry registration periodically until it succeeds or the application is foregrounded.
You can refer to this [library](https://github.com/umang91/fcm-client-lib) as an example of how to set up a retry mechanism. Alternatively, you can use this library instead of setting up the mechanism yourself.
* This is not an official library from MoEngage; this library is built by one of the developers at MoEngage.
* MoEngage SDK already has a retry mechanism built in to reduce token drop. We recommend letting the MoEngage SDK handle token registration to keep token drop minimal (typically under 2%). Refer to the [push configuration](/docs/developer-guide/android-sdk/push/basic/push-configuration) documentation to learn more about letting the MoEngage SDK manage your push token.
# How to share Android MoEngage SDK logs
Source: https://moengage.com/docs/developer-guide/android-sdk/troubleshooting-and-faqs/how-to-share-android-moengage-sdk-logs
Enable and share verbose MoEngage Android SDK logs with the support team to resolve issues faster.
Sometimes, our support team will ask for logs as the first step to resolve the issue faster. Logs can help us and in some cases you quickly identify the issue and share the exact solution to a problem.
In this document, we list a few steps that you can execute to share the MoEngage SDK logs with the MoEngage support team.
## Enable debug logs for Android
Use the snippet below to enable all MoEngage SDK-related logs.
```kotlin Kotlin theme={null}
import com.moengage.core.DataCenter
import com.moengage.core.MoEngage
import com.moengage.core.config.LogConfig
import com.moengage.core.LogLevel
import com.moengage.core.enableAllLogs
enableAllLogs()
val moEngage = MoEngage.Builder(this, "YOUR_WORKSPACE_ID", DataCenter.DATA_CENTER_X)
.configureLogs(LogConfig(logLevel = LogLevel.VERBOSE, isEnabledForReleaseBuild = true))
.build()
MoEngage.initialiseDefaultInstance(moEngage)
```
```java Java theme={null}
import com.moengage.core.MoEngage;
import com.moengage.core.config.LogConfig;
import com.moengage.core.LogLevel;
import com.moengage.core.MoESdkStateHelper;
MoESdkStateHelper.enableAllLogs();
// replace X with the correct data center
MoEngage moEngage = new MoEngage.Builder(this, "YOUR_WORKSPACE_ID", DataCenter.getDataCenterX())
.configureLogs(new LogConfig(LogLevel.VERBOSE, true))
.build();
MoEngage.initialiseDefaultInstance(moEngage);
```
`enableAllLogs()` only captures logs generated after it is called — it does not replay earlier logs. To turn off all logs, call `disableAllLogs()`. Set `isEnabledForReleaseBuild` to `false` and remove these calls before pushing the app to production.
## Enable debug logs for React Native
Follow the same steps as the [Android section](#enable-debug-logs-for-android) above. In addition, React Native provides log control at the TypeScript level. Update your MoEngage initialization code in `App.tsx` (or your app entry file) to pass a log configuration:
```typescript TypeScript theme={null}
import { MoEInitConfig, MoEPushConfig, MoEngageLogConfig, MoEngageLogLevel } from "react-native-moengage";
import ReactMoE from "react-native-moengage";
const moEInitConfig = new MoEInitConfig(
MoEPushConfig.defaultConfig(),
new MoEngageLogConfig(MoEngageLogLevel.VERBOSE, true)
);
ReactMoE.initialize("YOUR_WORKSPACE_ID", moEInitConfig);
```
Ensure to remove the above lines before the application is pushed to production.
## Android Studio
We will list the steps for sharing logs in Android Studio here, but similar steps apply to other IDEs.
1. First, connect your device to your laptop/PC.
2. Open Android Studio and click on Logcat.
3. Type MoE in the search field to filter only MoEngage logs(Please make sure the device is showing in device details and the correct device is shown).
4. MoEnage SDK-related logs should look like something shown in the picture below.
5. Copy the required lines or all lines in the Logcat, paste them on a text file, and share the file with MoEngage support team.
## Essential Logs
When browsing the logs, you can search for a few keywords to understand what is happening with the various services that MoEngage offers. Please note that verbose logs for MoEngage SDK has to be enabled for keywords to show up.
### Data tracking
**Search string** - sdk/report
**Occurrence** - You can see this string multiple times, and this log gets printed when the app is gone to the background and killed state. There are other cases when this log can be printed, but we will stick to these two conditions for now as we can easily perform background or kill actions. This line is seen 3 seconds after the app goes into the background or killed state.
**Description**
This log line indicates that the API call has been made to MoEngage servers and has succeeded. This API call is dependent on the proper integration of the lifecycle process. You might not see this line when the app goes into the background or killed state if the lifecycle observer isn't properly registered. Troubleshooting article - [How to debug lifecycle issues](/docs/developer-guide/android-sdk/troubleshooting-and-faqs/troubleshooting-and-faqs#why-is-lifecycle-process-library-important).
### Push Token
**Search string** - push\_id
**Occurrence** - This string **push\_id** can be seen in sdk/report API call as part of the params. There are other cases when push\_id is sent to MoEngage servers, but since we can easily take the app to background state, we are mentioning only this case here.
**Description**
If this parameter is present and not empty, it indicates that the push token was properly acquired by MoEngage SDK and synced with MoEngage backend servers. You might not see push\_id or empty value if there are problems with your FCM project setup. Please go through the [push integration](/docs/developer-guide/android-sdk/push/basic/push-configuration) steps properly.
### InApps - Sync
**Search string** - inapp/live and find get the line that has getResponse()
**Occurrence** - When the app is opened from the killed state. There are other cases when this API call is made, but we can easily kill the app and open it to see this call, so we are mentioning only this case here.
**Description**
This log line indicates that MoEngage SDK has made an API call to its backend servers to fetch the valid InApps or Nudges for the user. This API call is essential for InApps or Nudges to work as expected and depends on properly registering the lifecycle process. If you don't see this call once you open the app from killed state, go through the troubleshooting article here - [How to debug lifecycle issues.](/docs/developer-guide/android-sdk/troubleshooting-and-faqs/troubleshooting-and-faqs#why-is-lifecycle-process-library-important)
### InApps - display not shown
**Search string** - Cannot show
**Occurrence** - This string can be seen when the MoEngage SDK can't show the InApp.
**Description**
This log line indicates that Inpps aren't shown to the user, and the reason for not showing the InApp is also mentioned at the end of the log line.
### Exceptions
While browsing through MoEngage SDK logs, you might encounter error/exception logs, as shown in the above image. It's important to ensure no such logs for smoother integration of MoEngage SDK. Kindly contact our team via support tickets to identify the fix for these issues. We are also listing some common exceptions and their fixes [here](/docs/developer-guide/android-sdk/troubleshooting-and-faqs/troubleshooting-exceptions).
# How To Use the MoEngage SDK Logger?
Source: https://moengage.com/docs/developer-guide/android-sdk/troubleshooting-and-faqs/how-to-use-the-moengage-sdk-logger
Use the MoEngage SDK logger from your dashboard to debug implementation issues without sharing builds.
### Instructions:
Perform the following steps:
1. Log in to your MoEngage dashboard.
2. Go to the **Test and Debug** section.
3. Select the **SDK logger** tab, and a QR code will appear.
By default, the logger session remains active for two hours. You can extend it from your test mobile device.
The MoEngage SDK logger is supported from the following SDK versions:
* Android SDK 13.04.00
* React Native SDK 10.3.0
* Flutter SDK 9.0.0
* Capacitor SDK 5.0.0
* Cordova SDK 9.0.0
4. To start the logger, scan the QR code with your mobile device. A pop-up will appear with a link.
5. Open the link and click the **Click here** button. The link will either open the app or display a list of applications integrated with the MoEngage SDK.
6. Select the application you want to debug. A page displaying logger information will load.
7. Close and reopen the app. Perform the steps to replicate the issue.
8. Return to the dashboard and click **Refresh** to view a list of all active sessions.
9. Select the latest session identified by your device model.
10. If the logs do not appear, click **Refresh** again. Once the logs appear, click **Copy Session ID** and share the ID with the MoEngage support team for debugging.
A developer could check the logs for any errors or exceptions. You can also use the search field to find specific logs.
# Troubleshooting and FAQs
Source: https://moengage.com/docs/developer-guide/android-sdk/troubleshooting-and-faqs/troubleshooting-and-faqs
Find answers to common MoEngage Android SDK questions about debugging, logging, and integration.
In this article, we will address some common questions and some troubleshooting steps.
## How To Use the MoEngage SDK Logger?
SDKLogger will help you debug the MoEngage Android SDK implementation issues. You don't have to share the builds with our support team. You can just replicate the issue with the SDKlogger switched on, and share the session id with us for further help. We estimate that this logger will help us reduce the resolution times by about 2 days on an average.
The MoEngage SDK logger is supported from the following SDK versions:
* Android SDK 13.04.00
* React Native SDK 10.3.0
* Flutter SDK 9.0.0
* Capacitor SDK 5.0.0
* Cordova SDK 9.0.0
To use the MoEngage SDK logger, refer to [How To Use the MoEngage SDK Logger?](https://www.moengage.com/docs/developer-guide/android-sdk/troubleshooting-and-faqs/how-to-use-the-moengage-sdk-logger)
## How to enable MoEngage SDK to debug logs for signed/unsigned builds?
In order to see the verbose level logs, kindly add verbose log config in the initilisation.
```Kotlin Kotlin wrap theme={null}
val moEngage = MoEngage.Builder(
application = this,
appId = "YOUR_WORKSPACE_ID",
dataCenter = DataCenter.DATA_CENTER_X
)
.configureLogs(LogConfig(logLevel = LogLevel.VERBOSE, isEnabledForReleaseBuild = true))
.build()
```
```Java Java theme={null}
MoEngage moEngage = new MoEngage.Builder(this, "YOUR_WORKSPACE_ID", DataCenter.DATA_CENTER_X)
.configureLogs(new LogConfig(LogLevel.VERBOSE, true))
.build();
```
```Java ReactNative theme={null}
MoEngage moEngage = new MoEngage.Builder(this, "YOUR_WORKSPACE_ID", DataCenter.DATA_CENTER_X)
.configureLogs(LogConfig(LogLevel.VERBOSE, true))
```
```Java Flutter theme={null}
MoEngage moEngage = new MoEngage.Builder(this, "YOUR_WORKSPACE_ID", DataCenter.DATA_CENTER_X)
.configureLogs(LogConfig(LogLevel.VERBOSE, true))
```
Ensure to remove the above lines before the application is pushed to production.
## Why can't I see data on the dashboard?
If you don't see data on the dashboard check if you have implemented the below steps correctly
* Initialize the SDK correctly. For more information about initialization verification, refer to [SDK Initialization](https://www.moengage.com/docs/developer-guide/android-sdk/sdk-integration/basic-integration/sdk-initialization).
* Verify if you checking in the right environment.
Test/Debug Build --> Data flows to Test environment\
Signed/Live Build --> Data flows to Live environment.
## How to use Contextual InApps in React Nativ?
## How to use Contextual InApps for specific screens in the Flutter application?
## Why are events visible on test environment but not on the live environment?
If the data is visible on the Test environment and not in the Live environment, most likely you are using a test/debug build.\
The SDK detects whether the build is a debug/test build or a signed build. Based on the type of build data is sent to the respective environment.
Test/Debug Build --> Data flows to Test environment\
Signed/Live Build --> Data flows to Live environment.
## How does the SDK decide whether data should be sent to Test/Live Environment?
SDK detects the build type of the installed application and the basis that it decides whether data should be sent to the Test/Live Environment of the MoEngage Platform.
| Build Type | Environment |
| ---------- | ----------- |
| Debug | Test |
| Signed | Live |
## Why is lifecycle-process library important?
MoEngage SDK relies on lifecycle-process library for some key features like data syncing, inapps etc. Refer to [this](https://www.moengage.com/docs/developer-guide/android-sdk/sdk-integration/basic-integration/Install-Using-BOM) document to add the androidx libraries needed for lifecyle-process. If the features like in apps or data tracking aren't working as expected there are couple of troubleshooting steps we need to perform.
1. Check the MoEngage SDK logs and confirm that there is no exception related to the lifecycle process library.
2. Check your manifest file and ensure you aren't removing the Initialisation provider. You can read more here to properly handle the initialization provider [here](https://developer.android.com/jetpack/androidx/releases/lifecycle#2.4.0)
## Why is my App receiving the blank Push Notifications?
Blank push notifications can come up because of the following reasons :
1. Push from multiple servers not handled: Blank push notification if you don't handle push received from different servers correctly. Make sure the app does not try to show a notification if the notification is from MoEngage. For more information, refer to [Push Notification](https://www.moengage.com/docs/developer-guide/android-sdk/push/basic/push-configuration).
2. Custom Handling of notification: In case the app is doing some custom handling of notification make sure that silent push(used for uninstall ) is handled by the app.
## How to delete a notification from your inbox?
To delete a notification from Inbox, refer to [Notification Center](https://www.moengage.com/docs/developer-guide/android-sdk/push/optional/notification-center).
## Why are multiple notifications shown for one campaign?
Multiple Notifications can show up only if both the App and MoEngage SDK try to display the notification. If the payload is from MoEngage the App should pass the payload to the MoEngage SDK and not take any other action on it.\
For more information, refer to [Push Notification](https://www.moengage.com/docs/developer-guide/android-sdk/push/basic/push-configuration).
## How to fix multiple receivers?
Multiple receivers are the scenario where the manifest contains multiple services with the intent filters: **com.google.firebase.MESSAGING\_EVENT**\
Ideally, your manifest file should contain only one service with the above intent filter. For more information, refer to [Push Notification](https://www.moengage.com/docs/developer-guide/android-sdk/push/optional/notification-center).
## Why does sending push fail?
Sending push can fail because of the following reasons :
1. No Active device token - Push token not passed to MoEngage SDK or refresh token not passed. For more information about how to pass push tokens, refer to [Push Notification](https://www.moengage.com/docs/developer-guide/android-sdk/push/basic/push-configuration).
2. Not Registered - The application has been uninstalled hence cannot send push. Please re-install the application and try.
3. Mismatch Sender Id - The sender id and Server key provided are from different GCM/FCM projects. Please check the sender id and Server key. In the case of an FCM project sender-id is part of the `google-services.json` file.
4. Invalid Registration - The format of the push token passed is not correct. Please check the token passing logic.
## Why push is not visible even after it was successfully sent?
The following are the possible reasons for the push not being shown even after it was successfully sent :
* Push Payload not passed to MoEngage - This can happen when the app has its own push receiver and has not passed the payload to the MoEngage SDK. For more information, refer to [Push Notification](https://www.moengage.com/docs/developer-guide/android-sdk/push/basic/push-configuration).
* Internet Connectivity - Please check your internet connectivity on the device. We would recommend you to toggle the network connection once and try.
* Device-Specific Issue - Some of the OEMs force stop the app when the application is removed from the recent/overview screen. Please ensure the application is running in the background.
* Build does not belong to the correct environment - The SDK detects whether the build is a debug/test build or a signed build. Based on the type of build data is sent to the respective environment.
* Small icon not added - To post a notification small icon is mandatory. Please make sure you have added the meta-data required for push notification. For more information, refer to [Push Notification](https://www.moengage.com/docs/developer-guide/android-sdk/push/basic/push-configuration).
Test/Debug Build --> Use Test Environment to send notifications to your device.\
Signed/Live Build --> Use Live Environment to send notifications to your device.
## Why push is visible but clicks are not recorded?
The following are the reasons for click events not being recorded with MoEngage :
1. Push Payload not passed to SDK - The push payload is consumed by the application and the application takes care of showing push. This is based on the type of library you are using to pass the payload to MoEngage SDK.
2. Data not synced to MoEngage Server - Click attribution data would be sent to the MoEngage server only when the application is sent to the background. Please make sure that the application is sent to the background and wait for a couple of minutes.
## Why do we recommend letting MoEngage SDK handle push registration?
Push token registration is very important for sending push notifications.\
Push registration consists of two things - Token Registration and Refreshing Token\
Token registration can fail due to poor internet connection or null returned by the registration API. In such cases, one should retry registering for a push after an exponential backoff time rather than waiting for the next app to open.\
Refresh Token can be missed out because of some OEM level customizations. Many Chinese OEMs force stops the app once it is removed from the recent. This results in missing token refresh callbacks.\
MoEngage SDK has fallbacks to overcome the above issue. Hence, we suggest using MoEngage's Push Registration mechanism.\
MoEngage SDK provides a callback using which the app can get the push token. For more information, refer to [Push Registration by MoEngage](https://www.moengage.com/docs/developer-guide/android-sdk/push/basic/push-configuration).
## Android Vitals says MoEAlarmReceiver is causing excessive wake-ups. Why is MoEngage using Alarms and waking up devices?
Android Vitals report all alarms used by the app. Generally, alarms are used to trigger background tasks like downloading/uploading data to the server, which wakes up the CPU if the device is in an idle state and causes battery drain.\
MoEngage SDK MoEAlarmReceiver is used to send tracked events and attributes to the MoEngage Server. This alarm is triggered within 3-5 seconds after the application goes to the background. Since the CPU does not sleep so quickly no additional battery drain is done here. Our SDK intentionally delays this sync by 3-5 seconds to ensure data accuracy due to certain customizations done by OEMs without the delay data accuracy becomes a problem.
## How can you get extras after redirection?
All the extra parameters passed along while campaign creation will be a part of the bundle extras if the activity is inflated via the activity name. If the activity is inflated using a deep link URL all extras will be a part of the query params of the URL.
## What is a token drop and how to fix it?
For more information, refer to the [How to fix Token Drop?](https://www.moengage.com/docs/developer-guide/android-sdk/troubleshooting-and-faqs/how-to-fix-token-drop)
## Failed resolution of: Landroidx/lifecycle/ProcessLifecycleOwner
```Java Java wrap theme={null}
java.lang.NoClassDefFoundError: Failed resolution of: Landroidx/lifecycle/ProcessLifecycleOwner;
at com.moe.pushlibrary.MoEHelper.r(:1005)
at com.moengage.core.MoEngage.a(:210)
```
If you are seeing the above stack trace you have missed out on adding the `androidx.lifecycle:lifecycle-process` dependency in your application.\
Add the `androidx.lifecycle:lifecycle-process` and the issue should be fixed. You can refer to the [SDK Integration](https://www.moengage.com/docs/developer-guide/android-sdk/sdk-integration/basic-integration/Install-Using-BOM) documentation to know more about the dependencies required by the SDK.
## Why are some of the components marked as `export=true` in the case of Xiaomi Push? Is it a security concern?
The following components need the flag export=true, otherwise, it will affect the basic push function:
`com.xiaomi.mipush.sdk.PushMessageHandler`
* On clicking the notification in the notification bar, before jumping to the app UI interface, the SDK first parses the intent content through PushMessageHandler and execute startActivity() to jump;
* The intent content is encrypted, and the forged intent cannot penetrate the decryption link, and there is no security problem;
`com.xiaomi.push.service.receivers.NetworkStatusReceiver`
* Receives system network change broadcast, notify and change the long connection status; because it is a system broadcast, there is no need to worry about the broadcast being forged;
`com.moengage.mi.MoEMiPushReceiver`
* Receives the result callback of registration, set an alias, subscribe, and other interfaces;
* Added permission protection, intent encryption protection, forged intents cannot penetrate the decryption link, and there is no security problem.
## What is MoEDebuggerActivity?
`MoEDebuggerActivity` powers on-device debugging for the MoEngage SDK. It lets anyone with access to your MoEngage dashboard capture live SDK logs from a specific device without shipping a new build — useful for validating a new integration or diagnosing an issue with push, in-app messages, data tracking, or initialization. Opening a debug link for that device, generated from the MoEngage dashboard, launches this activity to enable, extend, or stop a logging session.
**Can it be disabled remotely?** No. Since the component is compiled into the app, it cannot be turned off for users who have already downloaded that version.
**To exclude it from future builds:** If this doesn't fit your organization's security or compliance policies, exclude the `sdk-debugger` module in your app's `build.gradle`:
```kotlin theme={null}
implementation(platform("com.moengage:android-bom:")) {
exclude(group = "com.moengage", module = "sdk-debugger")
}
```
This removes the `sdk-debugger` module from your app going forward, along with the ability to use this debugging capability for that app.
## Will initializing the SDK on the main thread affect my application start-up time?
No, the initialization API is The initialization approximately around 10-15 milliseconds on average. Refer to the [SDK Performance](https://www.moengage.com/docs/developer-guide/android-sdk/performance/sdk-performance) document for more details.
## Why MoEngage SDK is using setAllowFileAccess(true) in WebView settings?
MoEngage SDK uses WebView to load and display HTML InApps. Whenever ***MoEInAppHelper.getInstance().showInApp(context)*** is called, SDK downloads the dynamic Images, CSS & Other files used in the HTML InApps and stores them in the internal app storage, to load these assets into WebView from local storage setAllFileAccess(true) is needed.
## How can we disable/enable JavaScript in WebView?
By default, JavaScript usage is enabled in WebView.
```Kotlin Kotlin wrap theme={null}
configureJavascriptUsage(JavaScriptConfig(isJavaScriptEnabled))
```
```Java Java theme={null}
import static com.moengage.core.MoESdkStateHelper.configureJavascriptUsage;
configureJavascriptUsage(new JavaScriptConfig(isJavaScriptEnabled));
```
If you disable the JavaScript usage in WebView then RichLanding URLs & Html InApp may not render correctly on the device.
## Why SDK initialization should be done on the main thread inside the onCreate() of the Application class?
In general, any SDK should be initialized before any API/method of the SDK is called. MoEngage SDK is no different, before calling any API of the MoEngage SDK it is important that the SDK is initialized. If the SDK is not initialized Any data passed is the called API would be rejected by the SDK or if any API is expected to return data it would return `null` or default values(like false or -1).
For ease of implementation, we recommend the application initializes the MoEngage SDK in the `onCreate()` of the Application class. When we say in the onCreate() on the main thread we suggest synchronously not inside any callback etc. By initializing the SDK this way you can always ensure the SDK is initialized and ready for use anywhere in the application irrespective of the previous state. The Android system invokes `onCreate()` as one of the first few things on process creation. Hence initializing it in the `onCreate()` ensures SDK is always initialized and ready to process any events or campaigns.
This is especially important for cases when the application is in the killed state and a Push Campaign is sent to the user (the battery optimizations done by the OS and OEMs apps are killed within an hour or so of non-usage).
We understand that application start-up time is very important and if the SDK takes time to initialize it could affect the application experience. The initialization time of the SDK is minimum, refer to the [performance matrix](https://www.moengage.com/docs/developer-guide/android-sdk/performance/sdk-performance) to know more. We are continuously working on improving this further.
#### Alternatively,
Given any constraints, you cannot initialize the SDK in the `onCreate()` — please ensure the SDK is initialized before any SDK API is called, for example, `passPushPayload()`, `trackEvent()`, etc.
If the SDK is not initialised method call would not be processed resulting in data being discarded or lower campaign delivery. SDK provides an API [isSdkInitialised()](https://moengage.github.io/android-api-reference/core/com.moengage.core/is-sdk-initialised.html) to check whether the SDK is initialized or not, use the API to check if the initialization is done and initialize if required.
If you are not initializing the SDK in the `onCreate()` of the Application class please ensure implementing the [IntentPreProcessingListener](https://moengage.github.io/android-api-reference/core/com.moengage.core.listeners/-intent-pre-processing-listener/index.html) and registering this listener using the [registerPreProcessingListener](https://moengage.github.io/android-api-reference/core/com.moengage.core/-mo-e-core-helper/register-pre-processing-listener.html) in the `onCreate()` and initialize the SDK in the `onIntentRecieved()` of the interface. This is important to process notification clicks when the application is in the killed state.
## How can I stack the notifications?
By default, Android replaces your old notification with a new one. If you want to show stacked notifications to the user, you can enable isMultipleNotificationInDrawerEnabled = true in the notification config during SDK initialization. Please refer to the [notification config](https://moengage.github.io/android-api-reference/core/com.moengage.core.config/-notification-config/index.html) for additional options.
## Should I stop GeoFencing once the app goes to the background?
MoEngage uses system triggers to detect the location of the user and when the user exits or enters the geofence. So, it's not advised to call to stop the geofence monitoring method when the app goes in the background. But if your business doesn't want to monitor in specific cases the geofence changes, then you can call to stop geofence monitoring. MoEngage doesn't additionally poll to detect the location changes of the user, so the battery isn't affected if you don't stop geofence monitoring on the App.
## Why is Timer Notification not working?
Ensure that you have added rich notification dependency and more importantly you need to add the following line in the manifest file. Please check the documentation [here](https://www.moengage.com/docs/developer-guide/android-sdk/push/optional/push-templates#schedule-exact-alarm-permission).
```xml wrap theme={null}
```
Starting Android 14, you need to get explicit alarm permission from the customer, read more about it [here](https://www.moengage.com/docs/developer-guide/android-sdk/push/optional/push-templates#schedule-exact-alarm-permission).
## How can I add custom sound for push notifications?
From Android O, you can create a notification channel to which a custom sound can be added. However, you must do three things before you can get custom sounds for your notifications.
1. Add a custom channel to your app.
2. Add a sound to this custom channel.
3. Add the channel name in the MoEngage dashboard and use it while sending notifications.
We are providing the code for creating a custom channel and adding sound here.
```Kotlin Kotlin wrap theme={null}
private fun createCustomNotificationChannel(channelName: String) {
val channel = NotificationChannel(channelName, channelName, NotificationManager.IMPORTANCE_HIGH)
val soundUri = Uri.parse("android.resource://" + App.application?.packageName + "/" + R.raw.sound_file)
// sound_file will be added in the App's code
soundUri?.let {
val audioAttributes = AudioAttributes.Builder()
.setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
.setUsage(AudioAttributes.USAGE_ALARM)
.build()
channel.setSound(soundUri, audioAttributes)
}
val manager = getSystemService(NOTIFICATION_SERVICE) as NotificationManager
manager.createNotificationChannel(channel)
}
```
## FAQs on the GAID policy changes.
### Which of the MoEngage SDK versions comply with the GAID policy changes?
| Android | ReactNative | Flutter | Cordova | Unity | Capacitor | Segment |
| --------------- | ----------- | ------- | ------- | ----- | --------- | ------- |
| 11.6.2 & >12.2+ | 7.4.1 | 4.2.0 | 7.3.3 | 2.3.0 | 1.0.2 | 6.2.0 |
### What is the GAID policy?
For information on the GAID policy, refer [here](https://www.moengage.com/docs/user-guide/data/privacy/android-advertising-id-tracking#what-is-the-policy).
### As a MoEngage client, how does the GAID policy impact me?
Please go through “**How does it affect my app?**” [here](https://www.moengage.com/docs/user-guide/data/privacy/android-advertising-id-tracking#what-is-the-policy).
### How do I track GAID?
Firstly, update the MoEngage SDK to a corresponding version of your framework. Note that you will need explicit consent from the user on GAID tracking.
### Can I be on a version \<11.6 if I am not tracking GAID?
No. When you try updating your app in the play store, Google flags your application for non compliance of the MoEngage SDK version. We recommend an update to the latest version of MoEngage SDK.
### Can I be on \<11.6 if I am not updating my app in the play store?
For now you can be on \<11.6.2. However, If and when Google starts scanning all the apps for invalid versions of SDK, they might flag your application for non compliance of the SDK version.
### What are the reasons for rejection and what should I do If my application gets rejected/warned by the Google Play store at the time of publishing?
We encourage developers (and others more broadly) to make sure they've addressed non-production tracks as well. Developers who receive a notification should ensure all active versions of the app in either track, production or non-production (Internal / Closed / Open testing), have moved off the non-aligned SDK version or removed the SDK. Developers can also resolve the issue by deactivating any tracks that contain an active version of the app that has not addressed the issue during new submission.
Share these standard step-by-step instructions to update a non-compliant version of an APK with developers.
1. Navigate to your Play Console.
2. Select the app.
3. Navigate to App bundle explorer.
4. Select the violating APK/app bundle's App version at the top right dropdown menu, and make a note of which releases they are under.
5. Go to the track with the violation.\
It will be one of these: Internal / Closed / Open testing or Production.
6. Click Create new release near the top right of the page(You may need to click Manage track first).\
If the release with the violating APK is in a draft state, discard the release.
7. Add the new version of app bundles or APKs.\
Make sure the non-compliant version of app bundles or APKs is under the Not included section of the current release.
8. Click Save. This saves changes made to your release.
9. When you've finished preparing your release, select Review release, and then proceed to roll out the release to 100%.
10. If the violating APK is released to multiple tracks, repeat steps 5-9 in each track.
For a more comprehensive overview, publishers can check out [this page](https://support.google.com/googleplay/android-developer/answer/10357403?hl=en) for detailed guidance on how to prepare and roll out a release.
# Troubleshooting Exceptions
Source: https://moengage.com/docs/developer-guide/android-sdk/troubleshooting-and-faqs/troubleshooting-exceptions
Fix common MoEngage Android SDK exceptions related to in-app messages, lifecycle, and compatibility.
In this article, we will provide a fix for common exceptions.
# InApps
```text wrap theme={null}
java.lang.UnsupportedOperationException: Library support not found: Image and gif require Glide library.
```
Fix - Ensure you add the glide library dependencies mentioned in [this document](/docs/developer-guide/android-sdk/troubleshooting-and-faqs/troubleshooting-in-app#in-apps-arent-rendering-on-the-screen-name-selected).
```text wrap theme={null}
InAppFileManager downloadAndSaveFiles() : java.io.FileNotFoundException: https://campaign-assets-01.moengage.com/inbound/inapp/html_inapp/campaigns/tbsa_02_uat_sit/1712731832516233_r0mai9/17127318325182123_2m2mil/#
```
Fix - HTML template is having some errors related to href links, Fix the template without any errors and you shouldn't see the above error in the logs.
# Lifecycle-process
```text wrap theme={null}
java.lang.NoSuchFieldError: No field Companion of type Landroidx/lifecycle/ProcessLifecycleOwner$Companion; in class Landroidx/lifecycle/ProcessLifecycleOwner; or its superclasses (declaration of 'androidx.lifecycle.ProcessLifecycleOwner' appears in /data/app/~~iQ1qllgGR8saPZQwzjFrZg==)
```
Fix - Ensure that Android core and lifecycle process libraries are compatible. You can find the right lifecycle process library version in the change log for your sdk. - For example - [React SDK changelog](/docs/release-notes/sdks/react-native)
# Troubleshooting Images
Source: https://moengage.com/docs/developer-guide/android-sdk/troubleshooting-and-faqs/troubleshooting-images
Troubleshoot common image rendering issues in Android push notifications including icon problems.
In this article, we will address common questions on image rendering in Android push notifications and how to fix them.
## Why is the small icon not rendering properly in notifications?
The small notification icon may appear as a box or circle in some cases, as in the following image.
To render the small notification icon properly, ensure that the small notification icon is:
* flat
* pictured face-on
* of white color on a transparent background.
For more information, refer to [Small Notification Icon Guidelines](/docs/developer-guide/android-sdk/push/basic/push-configuration) and [Android Notification Guidelines](https://m2.material.io/design/platform-guidance/android-notifications.html#anatomy-of-a-notification).
## Large Icon not rendering in Push Notifications
The large icon is not displayed in push notifications if
* The notification large icon is not in the initialization of the SDK. Please look at the [Push Configuration](/docs/developer-guide/android-sdk/push/basic/push-configuration) documentation to check how to set the large icon.
* The download for the large icon configured in the campaign failed.
* You have explicitly disabled the large icon display in the notification configuration while integrating the SDK. Check if you have set the value of ***isLargeIconDisplayEnabled*** as false in the *NotificationConfig* object while initializing the SDK. Ensure the value is not set to false, else the large icon will not be displayed.
* The large icon display was disabled in the template settings for the campaign. Please check your campaign.
* The template does not support large icon. Please check the campaign preview.
# Troubleshooting In-App
Source: https://moengage.com/docs/developer-guide/android-sdk/troubleshooting-and-faqs/troubleshooting-in-app
Troubleshoot issues with MoEngage in-app messages not rendering or displaying correctly on Android.
This article will look at some of the most frequent problems encountered while using In-Apps and how to solve them.
## In-Apps aren't rendering as expected.
There are could be multiple reasons for InApps not rendering.
1. MoEngage SDK doesn't show the In-App by default, like push notifications. When you want to show the in-app, you must call the following line of code to show inapp for app open and screen based inapps. The only exception is custom event in-Apps; you need not call the following method for custom event in-Apps.\
[*MoEInAppHelper.getInstance().showInApp(context)*](https://moengage.github.io/android-api-reference/inapp/com.moengage.inapp/-mo-e-in-app-helper/show-in-app.html)
2. InApps are not shown when delivery controls aren't met or when some activities are opted out of the in-app optout config while initialising MoEngage SDK. If you have SDK logs enabled, you can see the reason in them.
3. In-apps are **not** synced to the device because of lifecycle library integration problems. Use the steps given [here](/docs/developer-guide/android-sdk/troubleshooting-and-faqs/troubleshooting-and-faqs) to troubleshoot them.
4. Inapps couldn't be synced to the device because of the exception in some other library functionality of MoEngage, please check the error logs if you find anything here.
## In-Apps aren't rendering as expected on app open
When you create an In-app campaign with trigger criteria as **on App open**, you also need to ensure that the following code is called on the app's first screen open. You need to call this code in onResume() of the fragment or onStart() of the Activity.
[*MoEInAppHelper.getInstance().showInApp(context)*](https://moengage.github.io/android-api-reference/inapp/com.moengage.inapp/-mo-e-in-app-helper/show-in-app.html)
## In-Apps aren't rendering on the screen name selected
When you create an In-app campaign with trigger criteria as **on specific screen**, you also need to ensure that the following code is called on the screen name you selected. You need to call this code in onStart() of the Activity or onResume() of the fragment.
[*MoEInAppHelper.getInstance().showInApp(context)*](https://moengage.github.io/android-api-reference/inapp/com.moengage.inapp/-mo-e-in-app-helper/show-in-app.html)
## Test In-Apps showing "Something went wrong" error
There could be multiple reasons for this error.
* Glide dependency missing
To fix this error please ensure you are adding the glide libraries and also use MoEngage inapp version >= 7.1.4
### **Requirements for displaying images and GIFs in InApp**
Starting InApp version **7.0.0,** SDK requires [Glide](https://bumptech.github.io/glide/) to show images and GIFs in the in-apps. You need to add the below dependency in your **build.gradle** file.
```Groovy Groovy wrap theme={null}
dependencies {
...
implementation("com.github.bumptech.glide:glide:4.16.0")
}
```
# Troubleshooting Push Redirection
Source: https://moengage.com/docs/developer-guide/android-sdk/troubleshooting-and-faqs/troubleshooting-push-redirection
Resolve common issues with deep linking, push CTA buttons, and rich landing page redirection on Android.
This article will discuss the common issues faced during deep linking and clicking on push notifications.
## Deeplink Redirection isn't working
This could happen because you haven't configured an Activity in your manifest to handle the given deep link. Verify if the deep link is properly and is redirecting to the expected screen using the below adb command.
```text wrap theme={null}
adb shell am start
-W -a android.intent.action.VIEW
-d
```
If the navigation is not working as expected with the above command there is something wrong in the way the deep link is configured. Please refer to this [official documentation](https://developer.android.com/training/app-links/deep-linking) to configure deep links in your application.
## Click on the push notification CTA button isn't working
This can happen when you have a custom push listener apart from the default MoEngage handler. You would most likely be handling the default click on the push notification but did not implement the handling for clicking on the CTA button. Please add the CTA button click handling to ensure the behavior meets expectations.
## The rich landing page doesn't render the webpage / URL
As per Android official documentation, - WebView objects allow you to display web content as part of your activity layout but lack some of the features of fully-developed browsers.
Webview is used to load rich landing pages within the app, and since webview doesn't support all the features that a normal browser supports, there is a chance that your website isn't loading in the rich landing redirection.
To handle this case, kindly either make your web page compatible with Webview or redirect the user to the device browser using a custom push listener code.
## Click call backs aren't received on react-native, Flutter, Cordava
This can happen in hybrid frameworks implementation, and you have missed adding the initialization code.
MoEInitializer.initializeDefaultInstance(applicationContext, moEngage)
Kindly ensure the above line is in your code per the initialization instructions.
## Clicks on deep link open the app in Android 11 and the browser in Android 12
Starting in Android 12 (API level 31), a generic web intent resolves to an activity in your application only if your application is approved for the specific domain in that web intent. If your application isn't approved for the domain, the web intent resolves to the device's default browser. Please refer to the [official Google documentation](https://developer.android.com/training/app-links/verify-android-applinks) to know more on how to approve/verify your application for your domain.
# What Are the Scenarios Where the SDK Logger Is Not Usable?
Source: https://moengage.com/docs/developer-guide/android-sdk/troubleshooting-and-faqs/what-are-the-scenarios-where-the-sdk-logger-is-not-usable
Learn about scenarios where the MoEngage SDK logger cannot help, such as integration and UI issues.
The SDK logger may not function correctly in the following scenarios:
* **Basic integration issues**:
* **Improper SDK integration**: This occurs when the SDK is not integrated correctly.
* **Outdated SDK versions**: The SDK version is older than the following:
* Android SDK: Native 13.4.00
* React Native: 10.3.00
* Flutter: 9.0.0
* Cordova: 9.0.0
* Capacitor: 5.0.0
* **Integration misses**: The plugin is not being initialized in the locations (as per the requirement).
* **Data tracking**
* **Push notifications**:
* **Visibility or UI problems**: Push notifications are not rendering correctly on devices.
* **Image loading failures**: Images are not displayed in push notifications due to exceptions or unsupported image formats (for example, WebP Image file format).
* **PushAmp cases**: Situations where the system renders the push notification rather than the SDK rendering the same.
* **In-app messages**:
* **Display anomalies**: In-app messages or In-app nudges appear too small or large and do not match the dashboard specifications.
* **Behavioral conflicts**: In-app messages interfering with app functionality.
* **Context setting in hybrid apps**: Lack of screen tracking in hybrid frameworks as we only track Native screens.
* **Self-Handled In-app messages**: Integration validation through a call might be required to ensure accurate statistics tracking. The developer might not have integrated the required methods to track events and statistics.
* **Cards**:
* **Self-handled cards**: There could be potential integration oversights.
# Compliance
Source: https://moengage.com/docs/developer-guide/capacitor-sdk/compliance/compliance
Enable or disable data tracking and the MoEngage Capacitor SDK from the JavaScript layer.
Use the APIs below to control what the MoEngage SDK tracks, based on the consent a user has given.
## Enable or Disable Data Tracking
To stop the SDK from tracking custom events and user attributes, call `disableDataTracking()`.
```javascript JavaScript theme={null}
import { MoECapacitorCore } from 'capacitor-moengage-core'
MoECapacitorCore.disableDataTracking({ appId: "YOUR_WORKSPACE_ID" })
```
The SDK rejects all events and user attributes until you call `enableDataTracking()`. Data tracking is enabled by default.
```javascript JavaScript theme={null}
import { MoECapacitorCore } from 'capacitor-moengage-core'
MoECapacitorCore.enableDataTracking({ appId: "YOUR_WORKSPACE_ID" })
```
## Enable or Disable the SDK
To stop the SDK from tracking any user information or sending any data to MoEngage, call `disableSdk()`.
```javascript JavaScript theme={null}
import { MoECapacitorCore } from 'capacitor-moengage-core'
MoECapacitorCore.disableSdk({ appId: "YOUR_WORKSPACE_ID" })
```
All SDK APIs are non-operational until you call `enableSdk()`. The SDK is enabled by default, so call `enableSdk()` only if you disabled it earlier.
```javascript JavaScript theme={null}
import { MoECapacitorCore } from 'capacitor-moengage-core'
MoECapacitorCore.enableSdk({ appId: "YOUR_WORKSPACE_ID" })
```
These APIs are available from **capacitor-moengage-core** version **2.0.0**.
## Delete User Data
To delete the current user's profile from the MoEngage server, refer to [Delete User From MoEngage Server](/docs/developer-guide/capacitor-sdk/data-tracking/delete-user-from-moengage-server).
# Delete User From MoEngage Server
Source: https://moengage.com/docs/developer-guide/capacitor-sdk/data-tracking/delete-user-from-moengage-server
Delete the current user from the MoEngage server using the Capacitor SDK on Android.
This API is supported from **capacitor-moengage-core** version **3.1.0** and is only available for the Android platform and is a no-operation for other platforms.
To delete the current user from the MoEngage server use ***deleteUser()*** method as shown below
```javascript TypeScript theme={null}
import { MoECapacitorCore } from 'capacitor-moengage-core'
MoECapacitorCore.deleteUser({appId: "YOUR_WORKSPACE_ID"}, (userDeletionData) = {
// add your code to handle the callback.
console.log(this.tag + " deleteUser(): workspaceId: " + userDeletionData.accountMeta.appId + " isSuccess=" + userDeletionData.isSuccess)
})
```
# Enable Advertising Identifier Tracking
Source: https://moengage.com/docs/developer-guide/capacitor-sdk/data-tracking/enable-advertising-identifier-tracking
Enable advertising identifier tracking in your Capacitor app for accurate device analytics.
For accurate analytics around devices and tracking Re-installs, it is essential to track the Advertising Identifier.
## Add Ad Identifier Library
Add the below dependency in the application-level ***build.gradle*** file.
```groovy Groovy theme={null}
implementation("com.google.android.gms:play-services-ads-identifier:18.0.1")
```
To enable Advertising Identifier tracking use the *enableAdIdTracking()* method as shown below.
```javascript TypeScript theme={null}
import { MoECapacitorCore } from 'capacitor-moengage-core'
MoECapacitorCore.enableAdIdTracking({ appId: "YOUR_WORKSPACE_ID" });
```
Before you enable Advertising ID tracking please ensure the application is complying with the [Google Play Policy](https://support.google.com/googleplay/android-developer/answer/10144311) regarding Advertising ID tracking. Refer to our [help document](https://www.moengage.com/docs/user-guide/data/privacy/android-advertising-id-tracking) for more information on the policy.
In case, you need to disable advertising-id after enabling tracking use the following method.
```javascript TypeScript theme={null}
import { MoECapacitorCore } from 'capacitor-moengage-core'
MoECapacitorCore.disableAdIdTracking({ appId: "YOUR_WORKSPACE_ID" });
```
The above APIs are available only starting plugin version 1.0.2. In the older versions, Advertising Identifier tracking is enabled by default.
# Install/Update Differentiation
Source: https://moengage.com/docs/developer-guide/capacitor-sdk/data-tracking/install-update-differentiation
Set the app status as install or update in the MoEngage Capacitor SDK for migration tracking.
This is solely required for migration to the MoEngage Platform. We need your help to tell the SDK whether the user is a new user of your app or an existing user who has updated to the latest version. If the user was already using your application and has just updated to a new version which has MoEngage SDK it is an updated , call the below API.
```javascript TypeScript theme={null}
import { MoECapacitorCore, MoEAppStatus } from 'capacitor-moengage-core'
// For Existing user who has updated the app
MoECapacitorCore.setAppStatus({ appStatus: MoEAppStatus.UPDATE, appId: "YOUR_WORKSPACE_ID" });
```
In case it is a fresh install call the below API
```javascript TypeScript theme={null}
import { MoECapacitorCore, MoEAppStatus } from 'capacitor-moengage-core'
//For Fresh Install of App
MoECapacitorCore.setAppStatus({ appStatus: MoEAppStatus.INSTALL, appId: "YOUR_WORKSPACE_ID" });
```
# Setting Unique ID for SDK versions below 6.0.0
Source: https://moengage.com/docs/developer-guide/capacitor-sdk/data-tracking/setting-unique-id-for-sdk-versions-below-6.0.0
Set the user attribute unique ID and manage login and logout states using the legacy setUniqueId() API in Capacitor SDK versions below 6.0.0.
## Implementing Login/Logout
* It's important to set the User Attribute Unique ID when a user logs into your app.
* This merges the new user with the existing user, if any exists, and will help prevent the creation of unnecessary/stale users.
* Setting the Unique ID is a critical piece to tie a user across devices and installs/uninstalls as well across all platforms (i.e. iOS, Android, Windows, The Web). Set the **USER\_ATTRIBUTE\_UNIQUE\_ID** attribute as soon as the user is **logged in**. Unique ID can be something like an email ID, a username (unique), or a database ID or any Backend generated ID.
* Do not set this for the user who not logged in.
### Login
```javascript TypeScript theme={null}
import { MoECapacitorCore } from 'capacitor-moengage-core'
MoECapacitorCore.setUniqueId({ uniqueId: "abc@xyz.com", appId: "YOUR_WORKSPACE_ID"});
```
**Note:** The following values are not allowed in the UniqueID field: "unknown", "guest", "null", "0", "1", "true", "false", "user\_attribute\_unique\_id", "(empty)", "na", "n/a", "", "dummy\_seller\_code", "user\_id", "id", "customer\_id", "uid", "userid", "none", "-2", "-1", "2"
### Logout
The application needs to notify the MoEngage SDK whenever the user is logged out of the application. To notify the SDK, call the API whenever the user is logged out of the application.
```javascript TypeScript theme={null}
import { MoECapacitorCore } from 'capacitor-moengage-core'
MoECapacitorCore.logoutUser({ appId: "YOUR_WORKSPACE_ID"});
```
In case the application is registering for push token it should pass the new push token to MoEngage SDK after user logout. For more information about passing push tokens, refer to [Push Configuration for Android SDK](/docs/developer-guide/android-sdk/push/basic/push-configuration).
### Updating User Attribute Unique Id
Use the method *setAlias()* to update the user attribute unique id instead of *setUniqueId()* with a different value. Using the method *setUniqueId()* with a new value creates unintended users in MoEngage.
You can not use "moe\_" as a prefix while naming events, event attributes, or user attributes. It is a system prefix and using it might result in periodic blacklisting without prior communication.
Use the following helper methods to set User attributes like Name, Email, Mobile, Gender, etc.
```javascript TypeScript theme={null}
import { MoECapacitorCore, MoEProperties, MoEUserGender, MoEAppStatus } from 'capacitor-moengage-core'
MoECapacitorCore.setUserName({ userName: "John Doe", appId: "YOUR_WORKSPACE_ID" });
MoECapacitorCore.setFirstName({ firstName: "John", appId: "YOUR_WORKSPACE_ID" });
MoECapacitorCore.setLastName({ lastName: "Doe", appId: "YOUR_WORKSPACE_ID" });
MoECapacitorCore.setEmailId({ emailId: "johndoef@xyz.com", appId: "YOUR_WORKSPACE_ID" });
MoECapacitorCore.setMobileNumber({ mobileNumber: "1234567890", appId: "YOUR_WORKSPACE_ID" });
MoECapacitorCore.setGender({ gender: MoEUserGender.FEMALE, appId: "YOUR_WORKSPACE_ID" });
MoECapacitorCore.setBirthDate({ birthdate: "1970-01-01T12:00:00Z", appId: "YOUR_WORKSPACE_ID" });
MoECapacitorCore.setUserLocation({ location: { latitude: 25.2311, longitude: 73.1023 }, appId: "YOUR_WORKSPACE_ID" });
```
For setting other User Attributes you can use the generic method **setUserAttribute(key, value)**
To set custom user attributes, you will have to provide the attribute name as shown below:
```javascript TypeScript theme={null}
import { MoECapacitorCore } from 'capacitor-moengage-core'
// For generic user attributes
MoECapacitorCore.setUserAttribute({ name: "Attribute Name", value: "AttributeValue", appId: "YOUR_WORKSPACE_ID" });
// For Time attribute use ISO-8601 format
MoECapacitorCore.setUserAttributeDate({ name: "Date Attribute Name", value: "1970-01-01T12:00:00Z", appId: "YOUR_WORKSPACE_ID" });
// For Location, use MoEGeoLocation instance
MoECapacitorCore.setUserAttributeLocation({ name: "Location Attribute Name", location: { latitude: 25.23, longitude: 73.23 }, appId: "YOUR_WORKSPACE_ID" });
```
# Tracking Events
Source: https://moengage.com/docs/developer-guide/capacitor-sdk/data-tracking/tracking-events
Track user actions and event properties using the MoEngage Capacitor SDK for segmentation and campaigns.
SDK adheres to the MoEngage FUP policies. For more information, refer to the [Fair Usage Policy](https://www.moengage.com/docs/user-guide/data/key-concepts/fair-usage-policy-fup).
Tracking events is how you record any actions your users perform, along with any properties that describe the action. Every trackEvent call records a single user action. We recommend that you make your event names human-readable so that everyone on your team can know what they mean instantly.
Every **trackEvent()** method call expects 3 parameters. They are the event name, event attributes and an account identifier. Event attributes use **MoEProperties** as an instance that represents attributes of the event. Add all the additional information which you think would be useful for segmentation while creating campaigns.\
For example, the following code tracks an **Purchase** event of a product. We are including attributes like price, quantity, purchase date, and store location which describe the event we are tracking.
```javascript TypeScript theme={null}
import { MoECapacitorCore, MoEProperties } from 'capacitor-moengage-core'
const properties: MoEProperties = {
generalAttributes: [
{ name: "quantity", value: 1 },
{ name: "product", value: "iPhone" },
{ name: "currency", value: "dollar" }
{ name: "price", value: 699 }
{ name: "new_item", value: "iPhone" }
],
dateTimeAttributes: [
{ name: "purchase_date", value: "2020-06-10T12:42:10Z" }
],
locationAttributes: [
{ name: "store_location", value: { latitude: 90.00001, longitude: 180.00001} }
]
};
MoECapacitorCore.trackEvent({ eventName: "Purchase", eventAttributes: properties, appId: "YOUR_WORKSPACE_ID"});
```
* Event names should not contain any special characters other than "\_". It can contain just spaces and underscore. Also, it should not contain “between”, “greater”, “less”, “in\_the\_last”, “in\_the\_next”, “equal”, “contains”, “starts”, or “is\_not".
* You can not use "moe\_" as a prefix while naming events, event attributes, or user attributes. It is a system prefix and using it might result in periodic blacklisting without prior communication.
# Analytics
MoEngage SDK has started tracking user sessions and application traffic source. To learn more about how user session and application traffic source tracking works, refer to the following docs:
* [Session and Source Analysis](https://www.moengage.com/docs/user-guide/analyze/analytics/session-and-source/session-and-source-analysis)
* [Advanced Session and Source Analysis](https://www.moengage.com/docs/user-guide/analyze/analytics/session-and-source/advanced-session-and-source-analysis)
With user session tracking we have introduced the flexibility to selectively mark events as non-interactive.
## What is a non-interactive event?
Events that do not affect the session calculation in anyways are called non-interactive events. Non-interactive events have the following properties
* Do not start a new session.
* Do not extend the session.
* Do not have information related to a user session.
## How to mark an event as non-interactive?
To mark an event as a non-interactive set **isNonInteractive** property of the **MoEProperties** to **true** as shown below:
```javascript TypeScript theme={null}
import { MoECapacitorCore, MoEProperties } from 'capacitor-moengage-core'
const properties: MoEProperties = {
generalAttributes: [
{ name: "quantity", value: 1 },
{ name: "product", value: "iPhone" },
{ name: "currency", value: "dollar" }
{ name: "price", value: 699 }
{ name: "new_item", value: "iPhone" }
],
dateTimeAttributes: [
{ name: "purchase_date", value: "2020-06-10T12:42:10Z" }
],
locationAttributes: [
{ name: "store_location", value: { latitude: 90.00001, longitude: 180.00001} }
],
isNonInteractive: true
};
MoECapacitorCore.trackEvent({ eventName: "Purchase", eventAttributes: properties, appId: "YOUR_WORKSPACE_ID" });
```
# Tracking User Attributes and User Identity
Source: https://moengage.com/docs/developer-guide/capacitor-sdk/data-tracking/tracking-user-attributes
Track user attributes and manage login and logout states using the MoEngage Capacitor SDK.
MoEngage distinguishes between two types of user data:
* **Identifiers** — values that uniquely identify a user across devices and platforms. Use `identifyUser()` to set these.
* **User attributes** — properties you know about a user such as name, email, or plan type. Use `setUserAttribute()` to set these.
## Identity management
For SDK versions below [6.0.0](/docs/developer-guide/release-notes/capacitor-sdk#core-6-0-0), refer to [this document](/docs/developer-guide/capacitor-sdk/data-tracking/setting-unique-id-for-sdk-versions-below-6.0.0).
Setting identifiers is important to:
* Tie user behavior across platforms.
* Ensure unnecessary or stale users are not created.
* Identify users across installs and re-installs.
### Login with a single identifier
Call the API below to pass the identifier to the MoEngage SDK.
```javascript TypeScript theme={null}
import { MoECapacitorCore } from 'capacitor-moengage-core'
MoECapacitorCore.identifyUser({ identity: "identifier", appId: "YOUR_WORKSPACE_ID" })
```
* This method replaces the deprecated `setUniqueId()`. If you are using `setUniqueId()`, replace it with `identifyUser()`.
* The following values are not allowed in the identifier field: `unknown`, `guest`, `null`, `0`, `1`, `true`, `false`, `user_attribute_unique_id`, `(empty)`, `na`, `n/a`, `""`, `dummy_seller_code`, `user_id`, `id`, `customer_id`, `uid`, `userid`, `none`, `-2`, `-1`, `2`.
### Login with multiple identifiers
If your application has multiple identifiers for a user, pass all identifiers to the SDK using the API below.
```javascript TypeScript theme={null}
import { MoECapacitorCore } from 'capacitor-moengage-core'
MoECapacitorCore.identifyUser({ identity: { "identifierName1": "identifierValue1", "identifierName2": "identifierValue2" }, appId: "YOUR_WORKSPACE_ID" })
```
Use the standard identifier keys below when passing common attributes as identifiers:
| User attribute | Key |
| :------------- | :----- |
| ID | `uid` |
| Email | `u_em` |
| Gender | `u_gd` |
| Birthday | `u_bd` |
| Name | `u_n` |
| First name | `u_fn` |
| Last name | `u_ln` |
| Mobile number | `u_mb` |
For custom identifiers, use any key name that is not in the reserved keywords list.
**Behavior of multiple `identifyUser()` calls:**
* If you call `identifyUser()` without logging out first, the existing logged-in user's identifiers are updated.
* If you call `identifyUser()` multiple times with different identifier names, the SDK appends the new identifier to the already set identifiers. Refer to the [Identity resolution documentation](https://www.moengage.com/docs/user-guide/data/user-data/unified-identity-identity-resolution) to learn more.
* For workspaces with Identity resolution enabled, the SDK stores previous identifier values and detects changes when `identifyUser()` is called with new values.
If a network issue prevents the identifier from reaching the MoEngage server, the SDK caches the `identifyUser()` call and retries automatically once the network is available or the next time the app opens.
To enable or disable the SDK and data tracking, refer to [Compliance](/docs/developer-guide/capacitor-sdk/compliance/compliance).
**Behavior change in SDK 6.0.0:** The SDK no longer automatically logs out the previous user when a new user is detected. Call `logoutUser()` explicitly before identifying a new user to avoid data corruption.
### Logout
Call this API when the user logs out of your application.
```javascript TypeScript theme={null}
import { MoECapacitorCore } from 'capacitor-moengage-core'
MoECapacitorCore.logoutUser({ appId: "YOUR_WORKSPACE_ID" });
```
#### Logout callback listener
Clearing user data and resetting the SDK state is an asynchronous process. Wait for the SDK to complete logout before navigating the user away or clearing your app's local state.
Register a listener for the `logoutCompleted` event to detect successful logout. Wait for the callback to track any events/user attributes for the new user after the method is called.
```javascript TypeScript wrap theme={null}
import { MoECapacitorCore, MoELogoutCompleteData } from 'capacitor-moengage-core';
MoECapacitorCore.addListener("logoutCompleted", (data: MoELogoutCompleteData) => {
console.log("Received callback 'MoELogoutComplete', data: " + JSON.stringify(data));
// Safe to navigate to login screen or clear local app state
});
```
### Logout callback data
```javascript TypeScript wrap theme={null}
/**
* Data returned when a logout operation is successfully completed.
*/
export interface MoELogoutCompleteData {
/**
* Account information
*/
accountMeta: MoEAccountMeta;
/**
* Platform type
*/
platform: MoEPlatform;
}
```
## Setting user attributes
You cannot use `moe_` as a prefix when naming events, event attributes, or user attributes. It is a system prefix and using it may result in periodic blacklisting without prior communication.
Use the following methods to set standard user attributes such as name, email, and mobile number.
```javascript TypeScript theme={null}
import { MoECapacitorCore, MoEUserGender } from 'capacitor-moengage-core'
MoECapacitorCore.setUserName({ userName: "John Doe", appId: "YOUR_WORKSPACE_ID" });
MoECapacitorCore.setFirstName({ firstName: "John", appId: "YOUR_WORKSPACE_ID" });
MoECapacitorCore.setLastName({ lastName: "Doe", appId: "YOUR_WORKSPACE_ID" });
MoECapacitorCore.setEmailId({ emailId: "johndoe@xyz.com", appId: "YOUR_WORKSPACE_ID" });
MoECapacitorCore.setMobileNumber({ mobileNumber: "1234567890", appId: "YOUR_WORKSPACE_ID" });
MoECapacitorCore.setGender({ gender: MoEUserGender.FEMALE, appId: "YOUR_WORKSPACE_ID" });
MoECapacitorCore.setBirthDate({ birthdate: "1970-01-01T12:00:00Z", appId: "YOUR_WORKSPACE_ID" });
MoECapacitorCore.setUserLocation({ location: { latitude: 25.2311, longitude: 73.1023 }, appId: "YOUR_WORKSPACE_ID" });
```
For custom attributes, use the generic `setUserAttribute()` method:
```javascript TypeScript theme={null}
import { MoECapacitorCore } from 'capacitor-moengage-core'
// Generic user attribute
MoECapacitorCore.setUserAttribute({ name: "Attribute Name", value: "AttributeValue", appId: "YOUR_WORKSPACE_ID" });
// Date attribute — use ISO 8601 format
MoECapacitorCore.setUserAttributeDate({ name: "Date Attribute Name", value: "1970-01-01T12:00:00Z", appId: "YOUR_WORKSPACE_ID" });
// Location attribute
MoECapacitorCore.setUserAttributeLocation({ name: "Location Attribute Name", location: { latitude: 25.23, longitude: 73.23 }, appId: "YOUR_WORKSPACE_ID" });
```
### Custom boolean user attribute
**iOS only:** Starting from version **5.x.x** of `capacitor-moengage-core`, the default tracking format for custom boolean attributes changed from double to boolean. Use `MoEAnalyticsConfig` to configure this behavior.
```javascript TypeScript theme={null}
import { MoECapacitorCore, MoEAnalyticsConfig, MoEInitConfig } from 'capacitor-moengage-core'
const analyticsConfig: MoEAnalyticsConfig = { shouldTrackUserAttributeBooleanAsNumber: false };
const initConfig: MoEInitConfig = { analyticsConfig: analyticsConfig };
MoECapacitorCore.initialize({ appId: "YOUR_WORKSPACE_ID", initConfig: initConfig });
```
```javascript TypeScript theme={null}
// shouldTrackUserAttributeBooleanAsNumber: true → tracked as 1
MoECapacitorCore.setUserAttribute({ name: "Boolean Attribute True", value: true, appId: "YOUR_WORKSPACE_ID" });
// shouldTrackUserAttributeBooleanAsNumber: false → tracked as false
MoECapacitorCore.setUserAttribute({ name: "Boolean Attribute False", value: false, appId: "YOUR_WORKSPACE_ID" });
```
### Reserved keywords
Do not use the following keys when tracking user attributes:
* `USER_ATTRIBUTE_UNIQUE_ID`
* `USER_ATTRIBUTE_USER_EMAIL`
* `USER_ATTRIBUTE_USER_MOBILE`
* `USER_ATTRIBUTE_USER_NAME`
* `USER_ATTRIBUTE_USER_GENDER`
* `USER_ATTRIBUTE_USER_FIRST_NAME`
* `USER_ATTRIBUTE_USER_LAST_NAME`
* `USER_ATTRIBUTE_USER_BDAY`
* `USER_ATTRIBUTE_NOTIFICATION_PREF`
* `USER_ATTRIBUTE_OLD_ID`
* `MOE_TIME_FORMAT`
* `MOE_TIME_TIMEZONE`
* `USER_ATTRIBUTE_DND_START_TIME`
* `USER_ATTRIBUTE_DND_END_TIME`
* `MOE_GAID`
* `INSTALL`
* `UPDATE`
* `MOE_ISLAT`
* `status`
* `user_id`
* `source`
# Capacitor SDK Deprecated Versions
Source: https://moengage.com/docs/developer-guide/capacitor-sdk/deprecated-versions/deprecated-versions
Check the support status of MoEngage Capacitor SDK major versions: which are current, which are still supported, and which are deprecated, along with deprecation dates.
Use this page to check the support status of MoEngage Capacitor SDK major versions. It lists which versions are current, which are still supported, and which are deprecated, along with deprecation dates. This is the source of truth for Capacitor SDK version status. For how the lifecycle works and what deprecation means for your app, see the [SDK Deprecation Policy](/docs/developer-guide/sdk-lifecycle-and-policies/sdk-deprecation-policy). To set up the Capacitor SDK, see the [integration guide](/docs/developer-guide/capacitor-sdk/sdk-integration/basic/sdk-installation/framework-dependency).
## Version Support Status
**Current** — The latest major version. It receives new features, fixes, and support.
**Supported** — An older major version within its 3-year support window. It continues to receive support.
**Deprecated** — A version outside its support window. It no longer receives fixes or support.
| Major Version | Status | Deprecation Date | Notes |
| -------------------- | ---------- | ---------------- | -------------------------------------------------------------------------------------------------------------- |
| Core 7.x | Current | TBD | Latest major version. Receives new features, fixes, and support. |
| Core 5.x and above | Supported | TBD | Receives support. |
| Core 4.1.0 and below | Deprecated | August 2027 | Support, debugging, and fixes are no longer available. Data continues to flow. Upgrade to the current version. |
MoEngage supports each major version for 3 years from its initial release; deprecation then takes effect the following August. See the [SDK Deprecation Policy — Support Window](/docs/developer-guide/sdk-lifecycle-and-policies/sdk-deprecation-policy#support-window) for details. This table is updated when MoEngage announces further deprecations. To receive advance notice, subscribe to the SDK newsletter and ensure your workspace administrator email is current.
## Understanding Deprecated Status
A deprecated version continues to send and receive data. MoEngage does not block traffic. After the deprecation date, MoEngage no longer debugs, fixes, or supports issues specific to that version, and the resolution for any issue is to upgrade to the current version. All fixes ship in the current version only and are not available on supported or deprecated versions. For the complete policy, see the [SDK Deprecation Policy](/docs/developer-guide/sdk-lifecycle-and-policies/sdk-deprecation-policy).
## Upgrading to a Supported Version
* Refer to the [Capacitor SDK release notes](/docs/release-notes/sdks/capacitor) for the current major version changes.
* For help, contact your MoEngage Customer Success Manager (CSM) or the Support team.
## Frequently Asked Questions
Apps on deprecated versions continue to run and data continues to flow. However, MoEngage no longer provides support, debugging, or fixes for deprecated versions. Upgrading to the current or a supported version using the [Capacitor SDK release notes](/docs/release-notes/sdks/capacitor) ensures you receive continued support.
MoEngage sends advance notice 6 to 12 months before the deprecation date through email to workspace administrators, the quarterly SDK newsletter, in-dashboard notices, and SDK release notes. See the [SDK Deprecation Policy](/docs/developer-guide/sdk-lifecycle-and-policies/sdk-deprecation-policy) for the full notification schedule.
# InApp NATIV
Source: https://moengage.com/docs/developer-guide/capacitor-sdk/in-app-messages/inapp-nativ
Set up MoEngage in-app NATIV campaigns in your Capacitor app to show contextual messages to users.
InApp NATIV Campaigns target your users by showing a message while the user is using your app. They are very effective in providing contextual information and help to cross-sell/up-sell on desired screens of your app or/and on desired actions performed by the user in your app.
## Install Android Dependency
## Install using BOM
Integration using BOM is the recommended way of integration; refer to the[ Install Using BOM document](/docs/developer-guide/android-sdk/sdk-integration/basic-integration/Install-Using-BOM) . Once you have configured the BOM add the dependency in the *app/build.gradle* file as shown below:
```json build.gradle wrap theme={null}
dependencies {
...
implementation("com.moengage:inapp")
}
```
Once the BOM is configured, include the specific MoEngage modules required for the application. \
Note: Version numbers are not required for these dependencies; the BOM automatically manages them.
### **Requirements for displaying images and GIFs in InApp**
Starting InApp version **7.0.0,** SDK requires [Glide](https://bumptech.github.io/glide/) to show images and GIFs in the in-apps. You need to add the below dependency in your **build.gradle** file.
```Code Groovy theme={null}
dependencies {
...
implementation("com.github.bumptech.glide:glide:4.16.0")
}
```
# Display InApp
MoEngage refreshes the list of eligible In-App campaigns via a meta API call. During an active session, the list refreshes when the app returns from the background to the foreground (at most once every 15 minutes), and immediately when a new session starts or the app is killed and relaunched.
Call the **showInApp()** wherever InApp has to be shown in the app as shown below:
```javascript TypeScript theme={null}
import { MoECapacitorCore } from 'capacitor-moengage-core';
MoECapacitorCore.showInApp({ appId: "YOUR_WORKSPACE_ID" });
```
# Display Nudges
Starting with ***capacitor-moengage-core***version **6**\*\*.0.0,\*\*MoEngage InApp SDK supports displaying Non-Intrusive nudges.
To show a Nudge InApp Campaign call `showNudge()`
```javascript TypeScript theme={null}
import { MoECapacitorCore } from 'capacitor-moengage-core';
// Display Nudge on the any available position
MoECapacitorCore.showNudge({position: MoENudgePosition.Any, appId: "YOUR_WORKSPACE_ID" });
// Display Nudge on the specific position
MoECapacitorCore.showNudge({position: MoENudgePosition.Top, appId: "YOUR_WORKSPACE_ID" });
```
# InApp/Nudge Redirection default behavior
On clicking an Inapp or Nudge, MoEngage SDKs will handle **only rich landing navigation** redirection.
For the screen name and deep link redirection, your app code should redirect the user to the right screen or deep link. To handle the screen name and deep link redirection, you must implement inapp click callback methods. MoEngage SDK will just pass the inapp payload to this call back code. Implementation steps are mentioned in the InApp callback section of the Integration.
# Self-Handled InApps
## Single Self-Handled InApps
Self-handled In Apps are messages which are delivered by the SDK but displaying it has to be handled by the App.\
To get self-handled In-App call the below method.
```javascript TypeScript theme={null}
import { MoECapacitorCore } from 'capacitor-moengage-core';
MoECapacitorCore.getSelfHandledInApp({ appId: "YOUR_WORKSPACE_ID" });
```
The payload for self-handled in-app is returned via a callback. Register a callback as shown below.
```javascript TypeScript theme={null}
import { MoECapacitorCore, MoEInAppSelfHandledCampaignData } from 'capacitor-moengage-core';
MoECapacitorCore.addListener("inAppCampaignSelfHandled", (data: MoEInAppSelfHandledCampaignData) => {
console.log(" Received callback 'inAppCampaignSelfHandled', data: " + JSON.stringify(data))
});
```
## Multiple Self-Handled InApps
To fetch multiple self-handled in-apps, call the `getSelfHandledInApps()` method. This returns a promise containing the `MoEInAppSelfHandledCampaignsData` object.
```javascript TypeScript wrap theme={null}
import { MoECapacitorCore } from 'capacitor-moengage-core';
MoECapacitorCore.getSelfHandledInApps({ appId: "YOUR_WORKSPACE_ID" }).then((data) => {
console.log("Self Handled InApps Data:", JSON.stringify(data));
});
```
# Tracking Statistics
Since display, click, and dismiss for Self-Handled InApp is controlled by the application we need you to notify the SDK whenever the In-App is Shown, Clicked, or Dismissed. Below are the methods you need to call to notify the SDK. The campaign object provided to the application in the callback for self-handled in-app should be passed in as a parameter to the below APIs.
```javascript TypeScript theme={null}
import { MoECapacitorCore, MoEInAppSelfHandledCampaignData } from 'capacitor-moengage-core';
//Track self handled shown
MoECapacitorCore.selfHandledShown(selfHandledCampaignData)
//Track self handled widget clicked
MoECapacitorCore.selfHandledClicked(selfHandledCampaignData)
//Track self handled dismissed
MoECapacitorCore.selfHandledDismissed(selfHandledCampaignData)
```
# InApp Callbacks
The callbacks must be registered before inapps are displayed either via show methods or triggered events. Make sure you are calling `initialize()` the method of the plugin after you set up these callbacks. Refer [doc](/docs/developer-guide/react-native-sdk/sdk-integration/react-native/sdk-initialization/manual-initialization/framework-initialization) for more info.
We provide callbacks whenever an InApp campaign is shown, dismissed, or clicked you can register for the same as shown below. Use this call both Intrusive InApps and Non-Intrusive Nudge InApps to handle action.
```javascript TypeScript theme={null}
import { MoECapacitorCore, MoEInAppLifecycleData, MoEInAppNavigationData, MoEInAppCustomActionData } from 'capacitor-moengage-core';
MoECapacitorCore.addListener("inAppCampaignShown", (data: MoEInAppLifecycleData) => {
console.log(" Received callback 'inAppCampaignShown', data: " + JSON.stringify(data))
});
MoECapacitorCore.addListener("inAppCampaignDismissed", (data: MoEInAppLifecycleData) => {
console.log(" Received callback 'inAppCampaignDismissed', data: " + JSON.stringify(data))
});
MoECapacitorCore.addListener("inAppCampaignClicked", (data: MoEInAppNavigationData) => {
console.log(" Received callback 'inAppCampaignClicked', data: " + JSON.stringify(data))
});
MoECapacitorCore.addListener("inAppCampaignCustomAction", (data: MoEInAppCustomActionData) => {
console.log(" Received callback 'inAppCampaignCustomAction', data: " + JSON.stringify(data))
});
```
| Event Type | Event Name |
| -------------------------------- | ------------------------- |
| InApp Shown | inAppCampaignShown |
| InApp Clicked | inAppCampaignClicked |
| InApp Dismissed | inAppCampaignDismissed |
| InApp Clicked with Custom Action | inAppCampaignCustomAction |
# Contextual InApp
You can restrict the in-apps based on the user's context in the application apart from restricting InApp campaigns on a specific screen/activity. To set the user's context in the application use **setInAppContext()** API as shown below.
## Set Context
Call the below method to set the context, before calling **showInApp().**
```javascript JavaScript theme={null}
import { MoECapacitorCore } from 'capacitor-moengage-core';
// replace array elements with actual values.
MoECapacitorCore.setInAppContext({ contexts: ["c1","c2"] , appId: YOUR_WORKSPACE_ID });
```
## Reset Context
Once the user is moving out of the context use the **resetInAppContext()** API to reset/clear the existing context.
```javascript JavaScript theme={null}
import { MoECapacitorCore } from 'capacitor-moengage-core';
MoECapacitorCore.resetInAppContext({ appId: YOUR_WORKSPACE_ID });
```
# Payload Structure
```typescript TypeScript theme={null}
/**
* In-App lifecycle event camapaign data
*/
export interface MoEInAppLifecycleData {
/**
* Account information
*/
accountMeta: MoEAccountMeta;
/**
* In-App Campaign data
*/
campaignData: MoEInAppCampaignData;
/**
* Platform information
*/
platform: MoEPlatform;
}
/**
* Campaign data.
*/
export interface MoEInAppCampaignData {
/**
* Unique Identifier for the campaign
*/
campaignId: string;
/**
* Name given to the campaign while creation on the MoEngage Dashboard.
*/
campaignName: string;
/**
* Additional Meta data related to the campaign.
*/
campaignContext: MoEInAppCampaignContext;
}
/**
* In-App navigation event campaign data
*/
export interface MoEInAppNavigationData {
/**
* Account information
*/
accountMeta: MoEAccountMeta;
/**
* In-App Campaign data
*/
campaignData: MoEInAppCampaignData;
/**
* Navigation action data
*/
navigation: MoEInAppNavigation;
/**
* Platform Data
*/
platform: MoEPlatform;
}
/**
* In-App navigation action data
*/
export interface MoEInAppNavigation {
/**
* InApp Action type
*/
actionType: MoEInAppActionType;
/**
* Type of Navigation.
*/
navigationType: string;
/**
* Navigation URL
*/
navigationUrl: string;
/**
* Key-Value Pair entered on the MoEngage Platform during campaign creation.
*/
kvPair: Map;
}
/**
* In-App custom event campaign data
*/
export interface MoEInAppCustomActionData {
/**
* Account information
*/
accountMeta: MoEAccountMeta;
/**
* In-App Campaign data
*/
campaignData: MoEInAppCampaignData;
/**
* Custom Action data
*/
customAction: MoEInAppCustomAction;
/**
* Platform information
*/
platform: MoEPlatform;
}
/**
* InApp Action type
*/
export declare enum MoEInAppActionType {
NAVIGATION = "navigation",
CUSTOM = "custom"
}
/**
* In-App custom action data
*/
export interface MoEInAppCustomAction {
/**
* InApp Action type
*/
actionType: MoEInAppActionType;
/**
* Key-Value Pair entered on the MoEngage Platform during campaign creation.
*/
kvPair: Map;
}
export interface MoEInAppSelfHandledCampaignsData {
/**
* Account information
*/
accountMeta: MoEAccountMeta;
/**
* Array of self handled campaigns
*/
campaigns: Array;
}
/**
* Data for self handled campaign.
*/
export interface MoEInAppSelfHandledCampaignData {
/**
* Account information
*/
accountMeta: MoEAccountMeta;
/**
* In-App Campaign data
*/
campaignData: MoEInAppCampaignData;
/**
* SelfHandled data
*/
selfHandled: MoEInAppSelfHandledCampaign;
/**
* Platform information
*/
platform: MoEPlatform;
}
/**
* Self Handled campaign object
*/
export interface MoEInAppSelfHandledCampaign {
/**
* Self handled campaign payload.
*/
payload: string;
/**
* Interval after which in-app should be dismissed, unit - Seconds
*/
dismissInterval: number;
/**
* Should the campaign be dismissed by pressing the back button or using the back gesture.
* if the value is true campaign should be dismissed on back press.
*/
isCancellable: boolean;
/**
* Display rules for the campaign
*/
displayRules: MoEInAppDisplayRules;
}
/**
* Display rules for self handled campaign
*/
export interface MoEInAppDisplayRules {
/**
* Screen name where campaign should be displayed
*/
screenName: string;
/**
* List of contexts
*/
contexts: Array;
/**
* List of screen names
*/
screenNames: Array;
}
/**
* Data returned when a logout operation is successfully completed.
*/
export interface MoELogoutCompleteData {
/**
* Account information
*/
accountMeta: MoEAccountMeta;
/**
* Platform type
*/
platform: MoEPlatform;
}
```
# Handling Orientation Change
This is only for the Android platform
MoEngage SDK has to be notified when the device orientation changes for SDK to handle in-app display.
There are two ways to do it:
1. Add the API call in the Android native part of your app
2. Call MoEngage plugin's **onOrientationChanged()**
## Add the API call in the Android native part of your app
Notify the SDK when **onConfigurationChanged()** API callback is received in your App's Activity class.
```auto Java theme={null}
import android.content.res.Configuration;
import android.os.Bundle;
import com.getcapacitor.BridgeActivity;
import com.moengage.capacitor.MoECapacitorCorePlugin;
import com.moengage.capacitor.MoECapacitorHelper;
public class MainActivity extends BridgeActivity {
@Override public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
MoECapacitorHelper.INSTANCE.onConfigurationChanged();
}
}
```
## Call the MoEngage plugin's orientation change API
Call the below API to notify SDK of the orientation change.
```javascript TypeScript theme={null}
import { MoECapacitorCore } from 'capacitor-moengage-core';
MoECapacitorCore.onOrientationChanged();
```
# Android Notification Runtime Permissions
Source: https://moengage.com/docs/developer-guide/capacitor-sdk/push/basic/android-notification-runtime-permissions
Handle Android 13 notification runtime permissions in your Capacitor app using the MoEngage SDK.
Android 13 (API level 33) and higher supports [runtime permission](https://developer.android.com/guide/topics/permissions/overview#runtime) for sending [non-exempt](https://developer.android.com/develop/ui/views/notifications/notification-permission#exemptions) (including Foreground Services (FGS)) notifications from an app [POST\_NOTIFICATIONS](https://developer.android.com/reference/android/Manifest.permission#POST_NOTIFICATIONS). This change helps users focus on the notifications that are most important to them.
Refer to the [official documentation](https://developer.android.com/develop/ui/views/notifications/notification-permission) for more details.
The below-mentioned APIs are supported starting MoEngage core Android SDK version **12.3.01**
When an application runs on Android 13 and wants to show notifications to the user, it must request the user's notification permission. You have two options: let MoEngage handle permissions for you or handle the notification permission with your code.
* MoEngage handles Notification permission.
* You just have to call a single line of code mentioned on this page.
* You maintain the notification permission logic.
* Notify MoEngage SDK if permission to push notifications is granted.
We recommend you let MoEngage handle push notification permissions.
## Notify SDK of permission result
Once the application requests the user for notification permission notify the SDK of the user response using the below API. *If you are letting MoEngage SDK handle the notification permissions, you should ignore this step.*
```javascript TypeScript theme={null}
import { MoECapacitorCore } from 'capacitor-moengage-core'
MoECapacitorCore.pushPermissionResponseAndroid({isGranted: isGranted});
```
## Update the Permission request count(optional)
Once the application requests the user for notification permission, update the SDK of the request attempts.
**Why does the SDK require permission attempt count?**
SDK requires the attempt count to accurately track the number of times the permission request was attempted. If the user denies the permission request twice the application/SDK cannot request permission further i.e. if we request permission it would be automatically denied without the user seeing the request dialog. As an SDK we cannot be sure of the number of times the user has denied the permission we try to optimize the calls to request permission based on the attempt count.
```javascript TypeScript theme={null}
import { MoECapacitorCore } from 'capacitor-moengage-core'
MoECapacitorCore.updatePushPermissionRequestCountAndroid({appId: "YOUR_WORKSPACE_ID", count : count});
```
## Setup Notification Channels
If the application has already taken notification permission from the user call the below API to set up Notification Channels for showing push notifications.
```javascript TypeScript theme={null}
import { MoECapacitorCore } from 'capacitor-moengage-core'
MoECapacitorCore.setupNotificationChannelsAndroid();
```
## Let MoEngage SDK handle Notification permission
MoEngage SDK provides helper APIs to show the permission request to the end-user or navigate the user to the settings screen for enabling notifications.
Use the below API to show the permission request dialog to the user.
When using the below API SDK automatically tracks the response, sets up the required notification channel, etc, mentioned above.
```javascript TypeScript theme={null}
import { MoECapacitorCore } from 'capacitor-moengage-core'
MoECapacitorCore.requestPushPermissionAndroid();
```
## Navigate to Notification Settings (situational/optional)
Use the below API to show navigate the user to the Notification Settings for the application on the device.
*Note*: Below Android 8(API level 26) the user is directed to the Application Info screen of your application.
```javascript TypeScript theme={null}
import { MoECapacitorCore } from 'capacitor-moengage-core'
MoECapacitorCore.navigateToSettingsAndroid();
```
# Android Push Configuration
Source: https://moengage.com/docs/developer-guide/capacitor-sdk/push/basic/android-push-configuration
Configure Android push notifications in your Capacitor app including FCM setup and push registration.
# Basic Setup
Follow the basic setup outlined in this section to enable push notifications on an Android device using MoEngage.
* **FCM Setup on MoEngage Dashboard -** FCM Authentication is the method to enable sending push notifications to your app installed on Android devices. Use the methods mentioned in [this article](/docs/developer-guide/android-sdk/push/basic/fcm-authentication) to authenticate MoEngage to access Firebase services.
* **Adding metadata for push notification -** Set the small icon and large icon drawable and other options to handle push notifications using the methods available in [this article](/docs/developer-guide/components-for-sdk/push-notification/android-push-configuration-for-hybrid-applications#adding-metadata-for-push-notification).
* **Android Notification Runtime Permissions** - When the application is running on Android 13 to show notifications to the user, applications would need to request the user for notification permission. Refer to the methods available in [this article](/docs/developer-guide/react-native-sdk/push/basic/android-notification-runtime-permissions) to handle permission requests.
* **Push Registration and Receiving** - To use Push Notification in your React Native application, you must configure Firebase. Configuring Firebase steps will depend on how you want to integrate it. MoEngage recommends leaving the push handling to MoEngage SDK, as the best practices are properly integrated. You can also handle the push at your app level. In any case, look at the following section that applies to you and finish the integration steps.
**Add messaging service**\
You must add the messaging service to the Manifest file for MoEngage SDK to show the notifications. Refer to this document [here](/docs/developer-guide/android-sdk/push/basic/push-token-registration-and-display#push-token-registration-and-display-by-moengage-sdk).
**Callback on token registration (optional)**\
To get a callback whenever a new token is registered or refreshed, refer to the method [here](/docs/developer-guide/android-sdk/push/basic/push-token-registration-and-display#push-token-registration-and-display-by-moengage-sdk).
**Notification Clicked Callback**
To receive a callback whenever a push is clicked and for custom handling redirection, use the method [in this article](/docs/developer-guide/android-sdk/push/advanced/callbacks-and-customisation#notification-received-callback).
**How to opt out of MoEngage Registration?**\
The MoEngage SDK attempts to register for a push token; since your application handles push, you need to opt out of SDK's token registration using the method in [this article](/docs/developer-guide/android-sdk/push/basic/push-token-registration-and-display#pass-the-push-token-to-moengage-sdk).
**Pass the Push Token To MoEngage SDK** - After receiving the push token from FCM, use the method in [this article](/docs/developer-guide/android-sdk/push/basic/push-token-registration-and-display) to pass the Push Token to the MoEngage SDK to set it in the MoEngage platform.
**Passing the Push payload to the MoEngage SDK** - After receiving the push payload on the app, use the method in [this article](/docs/developer-guide/android-sdk/push/basic/push-token-registration-and-display#passing-the-push-payload-to-the-moengage-sdk)to send out push notifications to the device.
MoEngage recommends using the Android native APIs to pass the push payload to the MoEngage SDK instead of the React-Native/Javascript APIs. React-Native Engine might not get initialized if the application is killed or if the notification is not sent at a high priority.
* [Pass the Push Token To MoEngage SDK](/docs/developer-guide/capacitor-sdk/push/basic/android-push-configuration#passing-push-token)
* [Pass the Push payload to the MoEngage SDK](/docs/developer-guide/capacitor-sdk/push/basic/android-push-configuration#passing-push-payload)
* [Callbacks and customizations](/docs/developer-guide/react-native-sdk/push/basic/android-push-configuration#customizing-push-notification)
# Capacitor APIs
We highly recommend you to use the Android native APIs for passing the push payload to the MoEngage SDK instead of the Capacitor APIs. Capacitor Engine might not get initialised if the application is killed or if the notification is not sent at a high priority.
## Passing Push Token
```javascript TypeScript theme={null}
import { MoECapacitorCore } from 'capacitor-moengage-core'
// pass the push token as a string
passFcmPushToken({ token: "TOKEN", appId: "YOUR_WORKSPACE_ID" });
```
## Passing Push Payload
```javascript TypeScript theme={null}
import { MoECapacitorCore } from 'capacitor-moengage-core'
// pass the push payload object
passFcmPushPayload(payload: object, appId: "YOUR_WORKSPACE_ID" });
```
We highly recommend you to use the Android native APIs for passing the push payload to the MoEngage SDK instead of the Capacitor APIs. Capacitor Engine might not get initialised if the application is killed or if the notification is not sent at a high priority.
# Notification Runtime Permission
Android 13 (API level 33) and higher supports [runtime permission](https://developer.android.com/guide/topics/permissions/overview#runtime) for sending [non-exempt](https://developer.android.com/develop/ui/views/notifications/notification-permission#exemptions) (including Foreground Services (FGS)) notifications from an app `POST_NOTIFICATIONS`. This change helps users focus on the notifications that are most important to them.
Refer to the [official documentation](https://developer.android.com/develop/ui/views/notifications/notification-permission) for more details.
The below-mentioned APIs are supported from Plugin version **2.0.0**
When the application is running on Android 13 to show notifications to the user, applications would need to request the user for notification permission.
For applications integrating the MoEngage SDK, would need to
* Notify the SDK of the permission request's response from the user.
* If the application has already requested push permission(before MoEngage integration) help MoEngage set up notification channels for notification display.
## Notify SDK of permission result
Once the application requests the user for notification permission notify the SDK of the user response using the below API.
```typescript Typescript theme={null}
import { MoECapacitorCore} from 'capacitor-moengage-core'
MoECapacitorCore.pushPermissionResponseAndroid({ isGranted: true/false });
```
## Setup Notification Channels
If the application has already taken notification permission from the user call the below API to set up Notification Channels for showing push notifications.
```typescript Typescript theme={null}
import { MoECapacitorCore} from 'capacitor-moengage-core'
MoECapacitorCore.setupNotificationChannelsAndroid();
```
MoEngage SDK provides helper APIs to show the permission request to the end-user or navigate the user to the settings screen for enabling notifications.
Use the below API to show the permission request dialog to the user.
When using the below API SDK automatically tracks the response, sets up the required notification channel, etc, mentioned above.
```typescript Typescript theme={null}
import { MoECapacitorCore} from 'capacitor-moengage-core'
MoECapacitorCore.requestPushPermissionAndroid();
```
Use the below API to show navigate the user to the Notification Settings for the application on the device.
*Note*: Below Android 8(API level 26) the user is directed to the Application Info screen of your application.
```typescript Typescript theme={null}
import { MoECapacitorCore} from 'capacitor-moengage-core'
MoECapacitorCore.navigateToSettingsAndroid();
```
## Customizing Push notification
If required the application can customize the behavior of notifications by using Native Android code (Java/Kotlin). To learn more about the customization refer to the [Advanced Push Configuration](/docs/developer-guide/android-sdk/push/advanced/callbacks-and-customisation) documentation.Instead of extending ***PushMessageListener*** as mentioned in the above document extend ***PluginPushCallback.***
Refer to the below documentation for Push Amp+, Push Templates, and Geofence.
* [Push Amplification](/docs/developer-guide/android-sdk/push/optional/push-amplification)
* [Push Amp Plus](/docs/developer-guide/android-sdk/push/optional/push-amp-plus/push-amp-plus-integration)
* [Push Templates](/docs/developer-guide/android-sdk/push/optional/push-templates)
* [GeoFence Push](/docs/developer-guide/android-sdk/push/optional/location-triggered)
# iOS Push Configuration
Source: https://moengage.com/docs/developer-guide/capacitor-sdk/push/basic/ios-push-configuration
Configure iOS push notifications in your Capacitor app including APNS certificates and registration.
## APNS Certificate:
First, you will have to create an APNS certificate and upload to the dashboard to be able to send push notifications in iOS. Follow the steps below to do that:
* [Create an APNS certificate](/docs/developer-guide/ios-sdk/push/basic/apns-certificate-pem-file-legacy)
* [Convert the resultant certificate to .pem format](/docs/developer-guide/ios-sdk/push/basic/apns-certificate-pem-file-legacy)
* [Upload .pem file to MoEngage Dashboard](/docs/developer-guide/ios-sdk/push/basic/apns-certificate-pem-file-legacy)
## Adding Push Entitlement to your Project:
Once the APNS Certificate is uploaded, enable Push Entitlement in the Xcode project. For that select your app target, then go to Capabilities. Here enable the Push Notifications capability for your app as shown below:
## Uninstall Tracking:
We make use of silent pushes to track uninstalls. For tracking uninstalls of all the users, enable Remote Notification background mode in-app capabilities for the same as shown below:
## Push Registration:
After this you will have to register for push notification by using **registerForPush** method of the plugin as shown below :
```javascript TypeScript theme={null}
//This is only for iOS
import { MoECapacitorCore } from 'capacitor-moengage-core'
MoECapacitorCore.registerForPush();
```
**AppDelegate Remote notification methods will not be called**
The plugin gets all the remote notification-related callbacks, therefore you won't receive any of them in your AppDelegate. Therefore, you will have to add observers for the notifications provided by the plugin instead.
## Provisional Push Registration:
This feature is supported from version ***9.0.0*** of the plugin.
To register for provisional push notification, call `registerForProvisionalPush()` API of the plugin as shown below.
```javascript TypeScript theme={null}
import { MoECapacitorCore } from 'capacitor-moengage-core'
MoECapacitorCore.registerForProvisionalPush({ appId: YOUR_WORKSPACE_ID });
```
## Rich Push and Templates Support:
Please refer to the Native iOS SDK docs for supporting Rich Push(images/videos/audio in the notification) and Templates in the app:
* [Rich Push](/docs/developer-guide/ios-sdk/push/basic/ios-push-integration-tutorial#optional)
* [Push Templates](/docs/developer-guide/ios-sdk/push/optional/push-templates)
# Push Callback
Source: https://moengage.com/docs/developer-guide/capacitor-sdk/push/basic/push-callback
Set up listeners for push token generation and notification click events in the MoEngage Capacitor SDK.
# Configuring Push Callbacks
MoEngage Plugin provides listeners for push events. These events are a common trigger for both iOS and Android platforms. Refer to the below code to set the listener to the same:
Make sure you are calling **initialize()** method of the plugin to receive these callbacks. Refer [Initialise Capacitor Component](/docs/developer-guide/capacitor-sdk/sdk-integration/basic/sdk-installation/framework-dependency) for more info.
## Push Token Generated Observer
Add a listener to listen to the **pushTokenGenerated** as shown below.
```typescript TypeScript theme={null}
import { MoECapacitorCore, MoEPushTokenData } from 'capacitor-moengage-core'
MoECapacitorCore.addListener("pushTokenGenerated", (data: MoEPushTokenData) => {
console.log(" Received callback 'pushTokenGenerated', data: " + JSON.stringify(data))
});
```
## Notification Click Observers
Add a listener to listen to the **pushClicked** as shown below.
```typescript TypeScript theme={null}
import { MoECapacitorCore, MoEPushCampaignData } from 'capacitor-moengage-core'
MoECapacitorCore.addListener("pushClicked", (data: MoEPushCampaignData) => {
console.log(" Received callback 'pushClicked', data: " + JSON.stringify(data))
});
```
# Payload
PushToken received in the callback is a **MoEPushTokenData** instance with the following definition:
```typescript TypeScript theme={null}
/**
* Push token object
*/
export interface MoEPushTokenData {
/**
* Platform type
*/
platform: MoEPlatform;
/**
* Type of push service
*/
pushService: MoEPushService;
/**
* Push Token
*/
token: String;
}
export declare enum MoEPlatform {
iOS = "iOS",
ANDROID = "android"
}
export declare enum MoEPushService {
APNS = 0,
FCM = 1,
MI_PUSH = 2,
PUSH_KIT = 3
}
```
NotificationPayload received in the callback is a **MoEPushCampaignData** instance with the following definition:
```typescript TypeScript theme={null}
/**
* Push event data
*/
export interface MoEPushCampaignData {
/**
* Account information
*/
accountMeta: MoEAccountMeta;
/**
* Push campaign object
*/
pushCampaign: MoEPushCampaign;
/**
* Platform data
*/
platform: MoEPlatform;
}
/**
* Account Object
*/
export interface MoEAccountMeta {
/**
* Account identifier
*/
appId: string;
}
export interface MoEPushCampaign {
/**
* Is the click action a defualt action
*/
isDefaultAction: boolean;
/**
* Clicked Action data
*/
clickedAction: Map;
/**
* Key-Value Pair entered on the MoEngage Platform during campaign creation.
*/
payload: Map;
}
export declare enum MoEPlatform {
iOS = "iOS",
ANDROID = "android"
}
```
Payload Structure for `clickedAction` Map
```json JSON theme={null}
{
"clickedAction": {
"type": "navigation/customAction",
"payload": {
"type": "screenName/deepLink/richLanding",
"value": "",
"kvPair": {
"key1": "value1",
"key2": "value2",
...
}
}
}
}
```
`platform` - Native platform from which callback is triggered. Possible values - `android`, `ios`\
`isDefaultAction` - This key is present only for the Android Platform. It's a boolean value indicating if the user clicked on the default content or not. true if the user clicks on the default content else false.\
`clickedAction` - Action to be performed on notification click.\
`clickedAction.type` - Type of click action. Possible values `navigation` and `customAction`. Currently, `customAction` is supported only on Android.\
`clickAction.payload` - Action payload for the clicked action.\
`clickedAction.payload.type` - Type of navigation action defined. Possible values `screenName`, `deepLink`, `richLanding`. Currently, in the case of iOS, richlanding and deep-link URLs are processed internally by the SDK and not passed in this callback therefore possible value in the case of iOS is only `screenName`.\
`clickAction.value` - value entered for navigation action or custom payload.\
`clickAction.kvPair` - Custom key-value pair entered on the MoEngage Platform.\
`payload` - Complete campaign payload.
## Android Payload
If the user clicks on the default content of the notification the key-value pair and campaign payload can be found inside the `payload` key. If the user clicks on the action button or a push template action the action payload would be found inside `clickedAction`.\
You can use the `isDefaultAction` key to check whether the user clicked on the default content or not and then parse the payload accordingly.
## iOS Payload
In the case of iOS, you would always receive the key-value pairs with respect to clicked action in `clickedAction` property. Refer to this \[/developer-guide/ios-sdk/push/advanced/custom-notification-handling#notification-payload] for knowing the iOS notification payload structure.
# Location Triggered
Source: https://moengage.com/docs/developer-guide/capacitor-sdk/push/optional/location-triggered
Add geofence-based location-triggered push notifications to your Capacitor app using MoEngage.
# Installation
## Adding Geofence Plugin
Add **capacitor-moengage-geofence** plugin to the capacitor project as shown below :
```auto Shell theme={null}
$npm install capacitor-moengage-geofence
```
## Android Installation
## Install using BOM
Integration using BOM is the recommended way of integration; refer to the [Install Using BOM document](/docs/developer-guide/android-sdk/sdk-integration/basic-integration/Install-Using-BOM). Once you have configured the BOM add the dependency in the *app/build.gradle* file as shown below:
```json build.gradle wrap theme={null}
dependencies {
...
implementation("com.moengage:geofence")
}
```
Once the BOM is configured, include the specific MoEngage modules required for the application. \
Note: Version numbers are not required for these dependencies; the BOM automatically manages them.
## Manual Installation

Once you install the Flutter Plugin add MoEngage's native Android SDK dependency to the Android project of your application.\
Navigate to **android/app/build.gradle**. Add the MoEngage Android SDK's dependency in the **dependencies** block.
```css build.gradle theme={null}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation("com.moengage:geofence:$sdkVersion")
}
```
where **\$sdkVersion** should be replaced by the latest version of the MoEngage Geofence SDK
## iOS
In the case of iOS, the native dependency is part of the Geofence Cordova SDK itself, so there is no need to include any additional dependency for supporting Geofence.
## Configuration
### Start Geofence Monitoring
After integrating the geofence package call **startGeofenceMonitoring()** method to initiate the geofence module, this will fetch the geofences around the current location of the user. Please take a look at the [iOS doc](/docs/developer-guide/ios-sdk/push/optional/location-triggered) and [Android doc](/docs/developer-guide/android-sdk/push/optional/location-triggered) for more information on Geofence. By default, the geofence feature is not enabled. You need to call the \*\*startGeofenceMonitoring()\*\*to receive location-triggered push messages.
```auto Typescript theme={null}
import { MoECapacitorGeofence } from 'capacitor-moengage-geofence';
MoECapacitorGeofence.startGeofenceMonitoring({ appId: "YOUR_WORKSPACE_ID" });
```
### Stop Geofence Monitoring
If you want to stop the geofence monitoring or feature use the **stopGeofenceMonitoring()** API. This API will remove the existing geofences.
```auto Typescript theme={null}
import { MoECapacitorGeofence } from 'capacitor-moengage-geofence';
MoECapacitorGeofence.stopGeofenceMonitoring({ appId: "YOUR_WORKSPACE_ID" });
```
# Capacitor Sample App
Source: https://moengage.com/docs/developer-guide/capacitor-sdk/sample-app/capacitor-sample-app
Explore the MoEngage Capacitor sample application on GitHub as a reference for SDK integration.
The [MoEngage Capacitor Sample application](https://github.com/moengage/Capacitor-Sample) offers a useful reference point for integrating MoEngage into your Capacitor app.
## Next Steps
* [SDK Installation](/docs/developer-guide/capacitor-sdk/sdk-integration/basic/sdk-installation/framework-dependency)
* [Framework Initialization](/docs/developer-guide/capacitor-sdk/sdk-integration/basic/sdk-initialization/framework-initialization)
# JWT Authentication
Source: https://moengage.com/docs/developer-guide/capacitor-sdk/sdk-integration/advanced/jwt-authentication
Secure your MoEngage data collection by implementing JWT authentication in your application.
## Overview
JWT (JSON Web Token) authentication is a standard method for securely verifying user identity. By implementing JWT authentication, you add a critical layer of security to your data collection process with MoEngage.
The feature ensures that the data sent on behalf of your identified users is authentic and has not been tampered with. This security is achieved by requiring a token that is cryptographically signed by your own server, which prevents unauthorized users from impersonating your legitimate users.
**Prerequisites**
Before you begin the implementation, please ensure you meet the following requirements:
* Your application must use the MoEngage Capacitor SDK version [***7.1.0*** ](/docs/release-notes/sdks/capacitor#core-7-1-0) or higher to access the JWT authentication feature.
* You must have access to your MoEngage dashboard to manage public keys and configure the feature's enforcement settings. For detailed information on enforcement settings, [refer here](/docs/user-guide/settings/account/security/sdk-authentication#step-2-select-an-enforcement-mode).
The following diagram illustrates the interaction between your application, your server, the MoEngage SDK, and the MoEngage server:
## Integration
Follow these steps to integrate JWT authentication into your Capacitor application.
### Step 1: Enable JWT Authentication
#### Android
You can enable JWT authentication during SDK initialization by configuring the **NetworkAuthorizationConfig** property on the **MoEngage.Builder** object.
```kotlin Kotlin wrap theme={null}
`val moEngage = MoEngage.Builder(this, "YOUR_WORKSPACE_ID", DataCenter.DATA_CENTER_X) //Existing configurations
.configureNetworkRequest(
NetworkRequestConfig(
networkAuthorizationConfig = NetworkAuthorizationConfig(
isJwtEnabled = true,
NetworkAuthorizationConfig(isJwtEnabled = true)
)
)
)`
```
```java Java wrap theme={null}
`MoEngage.Builder moEngage = new MoEngage.Builder(this, "YOUR_WORKSPACE_ID", DataCenter.DATA_CENTER_X) //existing configurations
.configureNetworkRequest(new NetworkRequestConfig(new NetworkAuthorizationConfig(true)));`
```
#### iOS
You can enable JWT authentication during SDK initialization by configuring the **networkConfig** property on the **MoEngageSDKConfig** object.
```swift Swift wrap theme={null}
let sdkConfig = MoEngageSDKConfig(appId: "YOUR_WORKSPACE_ID", dataCenter: .YOUR_DATA_CENTER)
sdkConfig.networkConfig = MoEngageNetworkRequestConfig(authorizationConfig: MoEngageNetworkAuthorizationConfig(isJwtEnabled: true))
MoECapacitorInitializer.sharedInstance.initializeDefaultInstance(sdkConfig, andLaunchOptions: nil)
```
```objective-c Objective-C wrap theme={null}
MoEngageSDKConfig* sdkConfig = [[MoEngageSDKConfig alloc] initWithAppId:@"YOUR_WORKSPACE_ID" dataCenter:YOUR_DATA_CENTER];
sdkConfig.networkConfig = [[MoEngageNetworkRequestConfig alloc] initWithAuthorizationConfig:[[MoEngageNetworkAuthorizationConfig alloc] initWithIsJwtEnabled:YES]];
[[MoECapacitorInitializer sharedInstance] initializeDefaultInstance:sdkConfig andLaunchOptions:nil];
```
### Step 2: Pass the JWT to the SDK
Your application is responsible for managing the JWT lifecycle. The recommended flow is to fetch a token upon user app login and pass the token to the SDK. You should also check if the token has expired on subsequent app launches and fetch a new one if necessary.
Use the **passAuthenticationDetails** method to provide the token to the SDK.
```typescript TypeScript wrap theme={null}
import {
MoECapacitorCore,
MoEAuthenticationType,
MoEAuthenticationData,
MoEJwtAuthenticationData
} from 'capacitor-moengage-core';
MoECapacitorCore.passAuthenticationDetails({
appId: 'YOUR_WORKSPACE_ID',
authenticationData: MoEAuthenticationData
});
```
For detailed information, refer to [Interface and Enums](#interface-and-enums).
### Step 3: Register the listener and Handle Authentication Errors
The payload for authentication error data is returned via a callback. Register a callback as shown below and handle token validation errors that the MoEngage server returns. The SDK invokes this listener when an authentication error occurs, which allows your application to fetch and provide a new token.
```typescript TypeScript wrap theme={null}
import {
MoECapacitorCore,
MoEAuthenticationType,
MoEAuthenticationErrorData,
MoEJwtAuthenticationErrorData,
MoEJwtErrorCode
} from 'capacitor-moengage-core';
MoECapacitorCore.addListener('authenticationError', (error: MoEAuthenticationErrorData) => {
if (error.authenticationType === MoEAuthenticationType.JWT) {
const errorData = error.data as MoEJwtAuthenticationErrorData;
const jwtError = errorData.code;
const message = errorData.message;
// Take appropriate action based on jwtError
// e.g. fetch a new token and call passAuthenticationDetails again
}
});
```
For detailed information, refer to [Interface and Enums](#interface-and-enums).
## Interface and Enums
The following interfaces and enums define the data structures used by the JWT authentication methods described in this guide. Use them when constructing your token payload and handling errors.
```typescript TypeScript wrap theme={null}
//Interface for MoEAuthenticationData
interface MoEAuthenticationData {
authenticationType: MoEAuthenticationType
data: MoEAuthenticationDetails // for JWT, use MoEJwtAuthenticationData
}
//Enum for MoEAuthenticationType
enum MoEAuthenticationType {
JWT = 'JWT'
}
//Interface for MoEJwtAuthenticationData
interface MoEJwtAuthenticationData {
token: string;
userIdentifier: string;
}
//Interface for MoEAuthenticationErrorData
interface MoEAuthenticationErrorData {
accountMeta: MoEAccountMeta;
platform: MoEPlatform;
authenticationType: MoEAuthenticationType;
data: MoEAuthenticationErrorDetails; // for JWT, use MoEJwtAuthenticationErrorData
}
//Interface for MoEJwtAuthenticationErrorData
interface MoEJwtAuthenticationErrorData {
code: MoEJwtErrorCode;
token: string;
userIdentifier: string;
message: string;
}
//Enum for MoEAuthenticationType
enum MoEAuthenticationType {
JWT = 'JWT',
}
//Enum for MoEJwtErrorCode
enum MoEJwtErrorCode {
TimeConstraintFailure = 'TIME_CONSTRAINT_FAILURE',
DecryptionFailed = 'DECRYPTION_FAILED',
HeaderTypeIncompatible = 'HEADER_TYPE_INCOMPATIBLE',
PayloadContentMissing = 'PAYLOAD_CONTENT_MISSING',
InvalidSignature = 'INVALID_SIGNATURE',
IdentifierMismatch = 'IDENTIFIER_MISMATCH',
Unknown = 'UNKNOWN',
TokenNotAvailable = 'TOKEN_NOT_AVAILABLE',
}`
```
**Information**
* If an API request fails due to an authentication error, the SDK will not retry the request until your application provides a new token.
* After 10 consecutive authentication failures in a single session, the SDK will stop attempting to sync data until the next session begins. This counter resets after any successful sync.
* Upon user logout, if a data sync fails due to a JWT error, the pending data will be deleted, and no retry will be attempted.
# Android SDK Initialization
Source: https://moengage.com/docs/developer-guide/capacitor-sdk/sdk-integration/basic/sdk-initialization/android-sdk-initialization
Initialize the MoEngage Android SDK in your Capacitor app's Application class with your Workspace ID.
# Initializing the SDK
Initialize the SDK on the main thread inside onCreate() and not create a worker thread and initialize the SDK on that thread.
```Java Java theme={null}
import android.app.Application;
import com.moengage.capacitor.MoEInitializer;
import com.moengage.core.MoEngage;
import com.moengage.core.DataCenter;
public class MainApplication extends Application {
@Override public void onCreate() {
super.onCreate();
// this is the instance of the application class and "YOUR_WORKSPACE_ID" is the Workspace ID from the dashboard.
MoEngage.Builder moEngage = new MoEngage.Builder(this,"YOUR_WORKSPACE_ID", DataCenter.DATA_CENTER_X);
MoEInitializer.initialiseDefaultInstance(this, moEngage);
}
}
```
Following details of the different data centers you need to set based on the dashboard hosts
| Data Center | Dashboard host |
| -------------------------- | ------------------------- |
| DataCenter.DATA\_CENTER\_1 | dashboard-01.moengage.com |
| DataCenter.DATA\_CENTER\_2 | dashboard-02.moengage.com |
| DataCenter.DATA\_CENTER\_3 | dashboard-03.moengage.com |
| DataCenter.DATA\_CENTER\_4 | dashboard-04.moengage.com |
| DataCenter.DATA\_CENTER\_5 | dashboard-05.moengage.com |
Refer to the [API reference](https://moengage.github.io/android-api-reference/core/com.moengage.core/-mo-engage/-builder/index.html) for more information about the detailed list of possible configurations.
All the configurations are added to the builder before initialization. If you are calling initialize at multiple places, ensure that all the required flags and configurations are set each time you initialize to maintain consistency in behavior.
In case your application does not have an Application class yet navigate to the java source code inside the android platform folder and add the Application class file.
Make sure your application class is defined in the **AndroidManifest.xml** file as well.
# Register MoEngage's Plugin
Register the plugin in your **Activity** class's **onCreate()**.
```Java Java theme={null}
import com.getcapacitor.BridgeActivity;
import com.moengage.capacitor.MoECapacitorCorePlugin;
public class MainActivity extends BridgeActivity {
@Override protected void onCreate(Bundle savedInstanceState) {
//register the MoEngage Capacitor Plugin
registerPlugin(MoECapacitorCorePlugin.class);
super.onCreate(savedInstanceState);
}
}
```
# Exclude MoEngage Storage File from Auto-Backup
This is a mandatory integration step and is very essential to prevent your data from getting corrupted. Android's auto back-up service periodically backs up Shared Preference files, Database files, etc, more details [here](https://developer.android.com/guide/topics/data/autobackup). This backup results in MoEngage SDK's identifiers being backed up and restored after re-install.This restoration of the identifier results in your data being corrupted and users not being reachable via push notifications.
To ensure data is not corrupted after a backup is restored, opt out of MoEngage SDK's storage files. Refer to [Exclude MoEngage Storage File from the Auto-Backup](/docs/developer-guide/android-sdk/sdk-integration/basic-integration/exclude-mo-engage-storage-file-from-auto-backup) section of the documentation to learn more about which files to exclude.
# File Based Initialization
Source: https://moengage.com/docs/developer-guide/capacitor-sdk/sdk-integration/basic/sdk-initialization/file-based-initlialization/file-based-initialization
## Overview
Starting with [v7.0.0.](/docs/release-notes/sdks/capacitor#core-7-0-0), the MoEngage Capacitor SDK supports file-based initialization.
To streamline the integration process and minimize initialization errors, MoEngage supports Script-Based Initialization. This approach allows you to manage App IDs and configuration settings directly within native configuration files, keeping them separate from your application logic.
## Script-based Initialization
This approach outlines how to use the form-based interface to generate a validated code snippet for initialization and to access module-specific configurations.
Follow these steps to generate your initialization script:
1. Navigate to the [Initialization Website.](https://app-cdn.moengage.com/sdk/integration/config/index.html)
2. Configure the values based on your application requirements. Refer to the Configuration Parameters tables below.
3. Click **Generate Code** at the bottom of the form.
## Android Configuration (XML)
For Android, initialization is handled by placing an XML configuration file in the application's resource directory.
### Android Configuration Reference
Below is the comprehensive list of keys available for `moengage.xml`.
| Category | XML Key Name | Type | Description |
| :----------- | :---------------------------------------------------- | :------- | :---------------------------------------------------------------------------------------------- |
| **Core** | `com_moengage_core_workspace_id` | String | Specifies your App ID. This field is mandatory. |
| | `com_moengage_core_file_based_initialisation_enabled` | Boolean | Set to `true` to enable this feature. |
| | `com_moengage_core_data_center` | Integer | Default: `1`. For more info, refer [Data Center values](#data-center-values). |
| | `com_moengage_core_environment` | String | Supported values are: `default`, `live`, or `test`. |
| | `com_moengage_core_custom_base_domain` | String | Specifies the base custom proxy domain to route SDK network traffic through your own subdomain. |
| | `com_moengage_core_integration_partner` | String | Specifies the core integration partner. For example, `segment` or `mparticle`. |
| **Push** | `com_moengage_push_notification_small_icon` | Drawable | Resource ID for small icon. |
| | `com_moengage_push_notification_large_icon` | Drawable | Resource ID for large icon. |
| | `com_moengage_push_notification_color` | Color | Notification accent color. |
| | `com_moengage_push_notification_token_retry_interval` | Integer | Retry interval (in seconds) for token registration. |
| | `com_moengage_push_kit_registration_enabled` | Boolean | If `true`, SDK registers for push token. |
| **Logs** | `com_moengage_core_log_level` | Integer | `0` (No Log) to `5` (Verbose). Default: `3`. |
| | `com_moengage_core_log_enabled_for_release_build` | Boolean | If `true`, prints logs in release builds. |
| **Security** | `com_moengage_core_storage_encryption_enabled` | Boolean | Enables local storage encryption. |
| | `com_moengage_core_network_encryption_enabled` | Boolean | Enables payload encryption over the network. |
| **Sync** | `com_moengage_core_periodic_data_sync_enabled` | Boolean | Enables periodic data sync in the foreground. |
| | `com_moengage_core_background_data_sync_enabled` | Boolean | Enables periodic data sync in the background. |
| **In-App** | `com_moengage_inapp_show_in_new_activity_enabled` | Boolean | Required for specific TV/Android setups. |
**Troubleshooting**
If the XML file is missing or the `com_moengage_core_workspace_id` is empty, the SDK will throw a `ConfigurationMismatchError`.
### Add Configuration File
Place the generated `moengage.xml` file in your Capacitor Android project at `android/app/src/main/res/values/`.
## iOS Configuration (Info.plist)
For iOS, initialization is handled by adding a configuration dictionary to your `Info.plist`.
### iOS Configuration Reference
Below is the comprehensive list of keys available for the `MoEngage` dictionary.
| Category | Plist Key | Type | Description |
| :----------- | :----------------------------------- | :--------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| **Core** | `WorkspaceId` | String | Specifies your App ID. It is a Mandatory field. |
| | `IsSdkAutoInitialisationEnabled` | Boolean | Set to `true` to enable SDK auto initialisation. |
| | `DataCenter` | Integer | Specifies the Data Center value. This is a Mandatory field. The default value is *1*. For more info, refer to [Data Center values](#data-center-values). |
| | `IsTestEnvironment` | String / Boolean | Customer selected option (`true`/`false`). Default value is: `$(SWIFT_ACTIVE_COMPILATION_CONDITIONS)`. |
| | `CustomBaseDomain` | String | Specifies the base custom proxy domain to route SDK network traffic through your own subdomain. |
| | `IntegrationPartner` | String | Specifies your integration partner. For example, `segment` or `mparticle`. Default value: none. |
| | `AppGroupName` | String | Specifies the App Group name used for sharing SDK data. Default value: `""`. |
| **Logs** | `IsLoggingEnabled` | Boolean | Set to *true* to enable SDK logs. |
| | `Loglevel` | Integer | `0` to `5`. Default: `2`. |
| **Security** | `IsStorageEncryptionEnabled` | Boolean | Enables local storage encryption. Default value: `false`. |
| | `KeychainGroupName` | String | Specifies the keychain group name used for storing encryption keys. This is a mandatory field if `IsStorageEncryptionEnabled` is `true`. Default value: `""`. |
| | `IsNetworkEncryptionEnabled` | Boolean | Enables payload encryption. Default: `false`. |
| | `EncryptionEncodedTestKey` | String | Dashboard auto-populated string. Used if `IsNetworkEncryptionEnabled` is `true`. |
| | `EncryptionEncodedLiveKey` | String | Dashboard auto-populated string. Used if `IsNetworkEncryptionEnabled` is `true`. |
| **Sync** | `AnalyticsEnablePeriodicFlush` | Boolean | Enables periodic data flush. Default: `true`. |
| | `AnalyticsPeriodicFlushDuration` | Integer | Flush interval in seconds. Default: `60`. |
| **In-App** | `InAppDisplaySafeAreaInset` | Real | Decimal value representing safe area padding. Default: `0`. |
| | `InAppShouldProvideDeeplinkCallback` | Boolean | If `true`, provides callback on deeplink. Default: `false`. |
### Data Center Values
Configure the integer corresponding to your region. Incorrect values will result in data loss.
| Data Center | Dashboard host |
| ----------- | ------------------------------------------------------------- |
| 1 | [dashboard-01.moengage.com](http://dashboard-01.moengage.com) |
| 2 | [dashboard-02.moengage.com](http://dashboard-02.moengage.com) |
| 3 | [dashboard-03.moengage.com](http://dashboard-03.moengage.com) |
| 4 | [dashboard-04.moengage.com](http://dashboard-04.moengage.com) |
| 5 | [dashboard-05.moengage.com](http://dashboard-05.moengage.com) |
| 6 | [dashboard-06.moengage.com](http://dashboard-06.moengage.com) |
### Update Info.plist
1. Open your project's `Info.plist` (found in `ios/App/App/Info.plist`).
2. Create a new Top-Level Key named `MoEngage` of type `Dictionary`.
3. Add the configuration file content generated in the [Initialization Website.](https://app-cdn.moengage.com/sdk/integration/config/index.html)
The key `IsSdkAutoInitialisationEnabled` uses the British spelling ('s'). Ensure you use the exact key name shown below, or initialization will fail.
**XML Snippet Representation:**
```xml XML theme={null}
MoEngageWorkspaceIdYOUR_WORKSPACE_IDIsTestEnvironment$(SWIFT_ACTIVE_COMPILATION_CONDITIONS)|$(GCC_PREPROCESSOR_DEFINITIONS)DataCenter1CustomBaseDomaindata.example.comIsLoggingEnabled
```
## Framework Level Initialization
After you configure the native files, you need to trigger the initialization in both your native wrappers and your Capacitor framework.
### Android Native Setup
Before initializing the SDK from JavaScript, initialise the native module in your Android Application class. Add the following to `android/app/src/main/java\/MainApplication.java (or .kt)` inside `onCreate()`.
File-based init (reads configuration from res/values/ XML, e.g. moengage.xml):
```javascript Java theme={null}
import com.moengage.capacitor.MoEInitializer;
@Override
public void onCreate() {
super.onCreate();
// ... existing code
MoEInitializer.initialiseDefaultInstance(this);
}
```
```kotlin Kotlin theme={null}
import com.moengage.capacitor.MoEInitializer
override fun onCreate() {
super.onCreate()
// ... existing code
MoEInitializer.initialiseDefaultInstance(this)
}
```
### iOS Native Setup
Ensure your `AppDelegate.swift` triggers the SDK initialization using the default instance method, allowing it to read from your `Info.plist`.
```swift Swift wrap theme={null}
import UIKit
import Capacitor
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
MoECapacitorInitializer.sharedInstance.initializeDefaultInstance()
return true
}
}
```
### Initialize Capacitor Component
After native Android setup, initialize the plugin from your app entry or root component (e.g., `app.component.ts`, `main.ts`, or `index.ts/js`) o the bridge and initConfig are registered before you call other MoEngage Capacitor APIs.
```typescript TypeScript theme={null}
import { MoECapacitorCore } from 'capacitor-moengage-core';
await MoECapacitorCore.initialize({
appId: 'YOUR_WORKSPACE_ID',
initConfig: {
analyticsConfig: {
shouldTrackUserAttributeBooleanAsNumber: true,
},
// Optional — omit if you do not need push click callbacks from JS
pushConfig: {
shouldDeliverCallbackOnForegroundClick: true,
},
},
// Optional — queues some callbacks until app returns to foreground
lifecycleAwareCallbackEnabled: false,
});
```
## Migration and Precedence
To migrate from manual code-based initialization to the file-based approach, refer [here](/docs/developer-guide/capacitor-sdk/sdk-integration/basic/sdk-initialization/file-based-initlialization/migration-and-precedence).
## Environments (Test vs. Live)
You can configure Test/Live environments within these files.
* **Android:** Use the key `test`.
* **iOS:** Use `IsTestEnvironment`
# Migration And Precedence
Source: https://moengage.com/docs/developer-guide/capacitor-sdk/sdk-integration/basic/sdk-initialization/file-based-initlialization/migration-and-precedence
### Android Migration Steps
To migrate from manual code-based initialization to the XML file-based approach, follow the below steps:
1. **Add configuration file** :Add your generated `moengage.xml` (or any *.xml under res/values/ with the correct com\_moengage\_* resource entries) to the app module, in the `android/app/src/main/res/values/moengage.xml` path.
2. **Update the Application class** : In `android/app/src/main/java//MainApplication.java (or .kt)`, remove the `MoEngage.Builder` setup and call file-based initialization instead.
```javascript Java theme={null}
import com.moengage.capacitor.MoEInitializer;
@Override
public void onCreate() {
super.onCreate();
// ... existing code
MoEInitializer.initialiseDefaultInstance(this);
}
```
```kotlin Kotlin theme={null}
import com.moengage.capacitor.MoEInitializer
override fun onCreate() {
super.onCreate()
// ... existing code
MoEInitializer.initialiseDefaultInstance(this)
}
```
### iOS Migration Steps
To migrate from code-based initialization to the `Info.plist` based approach, follow these steps:
1. **Update Info.plist**: Add the required MoEngage configuration keys (e.g., WorkspaceId, DataCenter) inside the *MoEngage* key in your `Info.plist` file.
2. **Update AppDelegate**: Remove the existing initialization code (the manual MoEngageSDKConfig logic) from your `AppDelegate` class and replace it with the default instance initialization to enable reading from the `Info.plist` file.
```Swift Swift wrap theme={null}
import MoEngageSDK
override func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) - Bool {
// ... existing code
MoECapacitorInitializer.sharedInstance.intializeDefaultInstance()
return true
}
```
```objective-c objective-c wrap theme={null}
#import
import CapacitorMoengageCore
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
// ... existing code
[[MoECapacitorInitializer sharedInstance] intializeDefaultInstanceWithAdditionalConfig:[[MoEngageSDKDefaultInitializationConfig alloc] init]];
return true
}
```
### Precedence Rules
The source of configuration is determined by the initialization function called in your native code:
* **Android**:
* **File-Based Init:** Calling `MoEInitializer.initializeDefaultInstance(context)` instructs the SDK to look for and read the `moengage.xml` file.
* **Code-Based Init:** Calling `MoEInitializer.initialize(context, moEngage.Builder)` will initialize the SDK using the configuration object passed in the parameters, ignoring the XML file even if it exists.
* **iOS:** Auto-initialization (via `Info.plist`) occurs first. However, if you subsequently call the manual `initialize` method with a configuration object in your code, it will update the current instance configuration.
# Framework Initialization
Source: https://moengage.com/docs/developer-guide/capacitor-sdk/sdk-integration/basic/sdk-initialization/framework-initialization
Initialize the MoEngage Capacitor plugin in your Ionic app using the appropriate lifecycle callback.
# Initialize Plugin
Initialize the MoEngage Plugin by calling the **`MoECapacitorCore.initialize({ appId: "YOUR_WORKSPACE_ID", initConfig: initConfig});`**. In the case of Ionic-React initialize the plugin in the **`useIonViewWillEnter()`** callback, for Ionic-Angular initialize the plugin in **`ngOnInit()`**.
For more information, read [Ionic React Lifecycle](https://ionicframework.com/docs/react/lifecycle).
## iOS
Starting from version 5.x.x of **capacitor-moengage-core**, the default tracking for the custom boolean attribute will be changed to bool from double. To configure this, use ***MoEAnalyticsConfig*** and pass true to track the boolean as double or pass false to track it as bool.
```typescript Typescript theme={null}
import { MoECapacitorCore, MoEAnalyticsConfig, MoEInitConfig} from 'capacitor-moengage-core'
const analyticsConfig: MoEAnalyticsConfig = {shouldTrackUserAttributeBooleanAsNumber: true};
const initConfig: MoEInitConfig = {analyticsConfig: analyticsConfig};
MoECapacitorCore.initialize({ appId: "YOUR_WORKSPACE_ID", initConfig: initConfig });
```
Refer to the [doc](/docs/developer-guide/capacitor-sdk/data-tracking/tracking-user-attributes) for more info
Make sure you are setting the Push/InApp callback listeners before calling the **`initialize({ appId: "YOUR_WORKSPACE_ID", initConfig: initConfig })`**.
# iOS SDK Initialization
Source: https://moengage.com/docs/developer-guide/capacitor-sdk/sdk-integration/basic/sdk-initialization/ios-sdk-initialization
Initialize the MoEngage iOS SDK in your Capacitor app's AppDelegate with your Workspace ID.
To initialize the iOS Application with the MoEngage Workspace ID from Settings in the dashboard. In your project, go to AppDelegate file and call the initialize method of `MoECapacitorInitializer` instance in `applicationdidFinishLaunchingWithOptions()` method as shown below:
Sample code to initialize from `application:didFinishLaunchingWithOptions:` method in
```objectivec Swift theme={null}
import UIKit
import Capacitor
import CapacitorMoengageCore
import MoEngageSDK
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
let sdkConfig = MoEngageSDKConfig(appId: "YOUR_WORKSPACE_ID", dataCenter: DATA_CENTER)
MoECapacitorInitializer.sharedInstance.initializeDefaultInstance(sdkConfig, andLaunchOptions: launchOptions)
return true
}
}
```
# Data Center
In case your app wants to redirect data to a specific zone due to any data regulation policy please configure the zone in the MOSDKConfig object.
For more information on Data Center, refer [here](/docs/developer-guide/ios-sdk/sdk-integration/basic/data-center).
# Android SDK Installation
Source: https://moengage.com/docs/developer-guide/capacitor-sdk/sdk-integration/basic/sdk-installation/android-sdk-installation
Install the MoEngage Android SDK in your Capacitor project using BOM or manual dependency configuration.
# Configuring Build Settings
## Option 1: Using BOM (Recommended)

Use the Bill of Materials (BOM) to automatically manage compatible versions of the SDK modules.
```auto build.gradle theme={null}
dependencies {
...
// Import the MoEngage BOM
implementation(platform("com.moengage:android-bom:"))
// Add optional modules as needed
implementation("com.moengage:inapp")
}
```
Replace **\\`** with the latest BOM version. For more info on integration using BOM, refer [here](/docs/developer-guide/android-sdk/sdk-integration/basic-integration/Install-Using-BOM).
## Option 2: Manual Integration
### Add Maven Repository
Add *mavenCentral()* repository in the project-level ***build.gradle*** file. If not present already.
Path - ***android/build.gradle(.kts)***
```auto Groovy theme={null}
buildscript {
repositories {
mavenCentral()
}
}
allprojects {
repositories {
mavenCentral()
}
}
```
### Enable Java 8
The SDK target and source compatible with version 8 of the Java Programming Language. Enable Java 8 in the application ***build.gradle***if not done already.
Path - ***android/app/build.gradle(.kts)***
```auto build.gradle theme={null}
android {
...
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = '1.8'
}
}
```
```auto build.gradle.kts theme={null}
android {
...
compileOptions {
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = "1.8"
}
}
```
### Add Androidx Libraries
The SDK depends on a few Androidx libraries for its functioning, add the below Androidx libraries in your application's build.gradle file in the dependencies block if not done already.
Path - ***android/app/build.gradle(.kts)***
```auto Groovy theme={null}
dependencies {
...
implementation("androidx.core:core:1.9.0")
implementation("androidx.appcompat:appcompat:1.4.2")
implementation("androidx.lifecycle:lifecycle-process:2.7.0")
}
```
The MoEngage SDK depends on the **lifecycle-process** library for a few key features to work and the latest version of **lifecycle-process** depends on the **androidx.startup:startup-runtime** library. Hence do not remove the **InitializationProvider** component from the manifest. When adding other Initializers using the **startup-runtime** make sure the Initializer for **lifecycle-process** library is also added. Refer to the [documentation](https://developer.android.com/jetpack/androidx/releases/lifecycle#2.4.0) to know how to add the Initializer.
# Framework Dependency
Source: https://moengage.com/docs/developer-guide/capacitor-sdk/sdk-integration/basic/sdk-installation/framework-dependency
Add the MoEngage Capacitor plugin to your project and configure native platform dependencies.
Connect your IDE to the [MoEngage docs MCP server](/docs/developer-guide/connect-your-ide-to-moengage-docs) for accurate, context-aware SDK guidance inside your development environment. Your assistant searches this documentation directly, helping you move through the integration with confidence.
Capacitor is a cross-platform native runtime that makes it easy to build modern web apps that run natively on iOS and Android.
# Adding MoEngage Plugin
Add **capacitor-moengage-core** plugin to capacitor project as shown below :
```Shell Shell theme={null}
$npm install capacitor-moengage-core
```
Once the plugin is installed run ***npx cap sync*** to update capacitor native platform(s) and dependencies.
Follow the ***Capacitor*** framework's guidelines for adding plugins.
A working Sample App can be found [here](https://github.com/moengage/Capacitor-Sample).
# Integrate Native platforms
To install and integrate the respective platforms, follow the docs given below:
* [Android SDK Installation](/docs/developer-guide/capacitor-sdk/sdk-integration/basic/sdk-installation/android-sdk-installation)
* [iOS SDK Installation](/docs/developer-guide/capacitor-sdk/sdk-integration/basic/sdk-installation/ios-sdk-installation)
# iOS SDK Installation
Source: https://moengage.com/docs/developer-guide/capacitor-sdk/sdk-integration/basic/sdk-installation/ios-sdk-installation
Install the MoEngage iOS SDK in your Capacitor project by running the ionic capacitor sync command.
We have added our native SDK **MoEngage-iOS-SDK** as a dependency for **capacitor-moengage-core** plugin. Hence run **ionic capacitor sync** to add native SDK to your iOS Project.
Support for the Swift Package Manager is available starting with version 6.0.0.
# Troubleshooting and FAQs - Capacitor
Source: https://moengage.com/docs/developer-guide/capacitor-sdk/troubleshooting-and-faqs/troubleshooting-and-faqs-capacitor
Troubleshoot common Capacitor SDK issues including missing MoEngage logs and configuration problems.
## Why are you not able to see the MoEngage logs?
There could be couple of reasons for this -
1. Ensure you have enabled verbose logs in the application class of your project - you can read more information [here](/docs/developer-guide/android-sdk/troubleshooting-and-faqs/how-to-share-android-moengage-sdk-logs).
2. Ensure that the name of the application class in the manifest file in your android project is same as application name that is mentioned in your application file. check the image below for more understanding.
## What is MoEDebuggerActivity?
The MoEngage SDK bundles the native MoEngage Android SDK, so your Android build includes `MoEDebuggerActivity`, a component that supports on-device SDK debugging. Refer to [What is MoEDebuggerActivity?](/docs/developer-guide/android-sdk/troubleshooting-and-faqs/troubleshooting-and-faqs) to understand what it does.
To remove it from your app, add the following to your Android project's `AndroidManifest.xml`:
```xml theme={null}
```
# JavaScript Bridge for HTML In-Apps
Source: https://moengage.com/docs/developer-guide/components-for-sdk/javascript-bridge/javascript-bridge-for-html-in-apps
Use the MoEngage JavaScript bridge API to interact with the SDK from HTML in-app message templates.
HTML in-app messages for Android support a bridge interface for web apps to interact with the MoEngage SDK.
The bridge contains a set of APIs that can be accessed using a global variable `moengage`.
The below table lists all the JS Bridge methods and in the subsequent sections, each API is explained in detail.
| Method Name | Description |
| ----------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| moengage.dismissMessage() | Attach this method on your template for closing the in-app message |
| moengage.navigateToScreen("screen-name", "optional-data-json") | Use this method to navigate the user to a specific screen on click of a template element. Params: "screen-name" : name of the screen for redirection "optional-data-json" : additional key value pairs |
| moengage.openDeepLink("deeplink-url", "optional-data-json") | Use this method when you want to open a deeplink from your html in-app template "deeplink-url" : deeplink url "optional-data-json" : additional key value pairs |
| moengage.openRichLanding("richlanding-url", "optional-data-json") | Use this method when you want to open a URL inside the app's webview "richlanding-url" : url that you want to open "optional-data-json" : additional key value pairs |
| moengage.openWebURL("web-url", "optional-data-json"). | Use this method when you want to open a URL on the app's default browser "web-url" : url that you want to open "optional-data-json" : additional key value pairs |
| moengage.copyText("text-to-copy", "message") | Use this method to copy a coupon code or offer code from your html in-app template to clipboard "text-to-copy": This text will be copied to clipboard "optional-message": This message will be shown after copy |
| moengage.call("mobile-number") | Use this method to initiate a call from your html in-app template "mobile-number": This number will appear in the dialer |
| moengage.sms("mobile-number", "message") | Use this method to initiate an sms from your html in-app template "mobile-number": SMS will be sent to this "message": This message will be sent as the SMS |
| moengage.share("share-text") | Use this method to initiate the share action via any of the available apps on the device. |
| moengage.customAction("data-json") | Use this method to initiate a custom action defined by your app. "data-json": key value pairs |
| moengage.showPushOptIn() | This only for iOS and based on current notification status, either the notification permission pop is shown or will be taken to settings screen where notification status can be updated. |
| moengage.navigateToSettings() | Use this method to navigate to the settings screen of the device |
| moengage.trackEvent(eventName, generalAttrJson) | Use this method to track an event from html in-app template "eventName" : Name of the tracked event "generalAttrJson" : Event attributes that will be tracked along with the event |
| moengage.trackClick("widgetId") | Use this method to track clicks stats for campaign performance measurement. "widgetId": This is an optional argument and if passed will help populate the click distribution table. Effective when you have multiple CTAs in a single template |
| moengage.trackDismiss(widgetId) | Use this method to track dismiss stats for campaign performance measurement and identifying users who dismiss the in-app message "widgetId": This is an optional argument and if passed will help populate the click distribution table. Effective when you have multiple dismiss CTAs in a single template |
| moengage.setEmailId("value") | Use this method to capture the email\_id of your users. This will be saved in user profile automatically and can be used to send email campaigns. You can use this with your lead generation / signup forms. "value" : This is the email\_id of the user |
| moengage.setMobileNumber("value") | Use this method to capture the mobile number of your users. This will be saved in user profile automatically and can be used to send SMS campaigns. You can use this method with your lead generation / signup forms. "value" : This is the mobile number of the user |
| moengage.setUserName("value") | Use this method to capture the Name of your users. This will be saved in user profile automatically. You can use this method with your lead generation / signup forms. "value" : This is the username of the user |
| moengage.setFirstName("value") | Use this method to capture the First Name of your users. This will be saved in user profile automatically. You can use this method with your lead generation / signup forms. "value" : This is the first name of the user |
| moengage.setLastName("value") | Use this method to capture the Last Name of your users. This will be saved in user profile automatically. You can use this method with your lead generation / signup forms. "value" : This is the last name of the user |
| moengage.setUniqueId() | Use this method to set the Client ID of your users. |
| moengage.setAlias() | Use this method to update the existing Client ID of your users. |
| moengage.setGender() | Use this method to set the Gender of your users. |
| moengage.setBirthDate() | Use this method to set the Birthdate of your users. |
| moengage.setUserLocation(lat, lng) | Use this method to set the Location (lat,lng) of your users. |
| moengage.setUserAttribute(name,value) | Use this method to set custom attributes of your users when data type of the attribute is string, numeric or boolean. name : Name of the user attribute to be tracked value : Value of the tracked user attribute. |
| moengage.setUserAttributeDate(name,value) | Use this method to set custom attributes of your users when data type of the attribute is date name : Name of the user attribute to be tracked value : Value of the tracked user attribute in ISO 8601 format |
| moengage.setUserAttributeLocation(name,lat,lng) | Use this method to set custom attributes of your users when data type of the attribute is location name : Name of the user attribute to be tracked lat,lng : Location of the user |
| moengage.trackRating() | Use this method to capture the rating of your users from a rating template. |
The subsequent sections provide more details on how to use each API. The provided JS Methods are classified into two categories - JS Methods for Actions and JS Methods for Data Tracking
# Actions
The following sections will provide more details on the JS Methods for actions.
**JS Methods for Actions**
Integrate the JS Methods for Actions with your HTML Template to execute on-click actions like message close, navigation actions like re-direction to screens or webpages, or any other actions as supported with in-app campaigns.
# Dismiss In-App Message
To dismiss in-app messages use `moengage.dismissMessage()` API.
```javascript JavaScript theme={null}
moengage.dismissMessage();
```
Do not use other API when the `moengage.dismissMessage()`API is in use.
# Navigation Actions
## Navigate to Screen
You can open a screen using `moengage.navigateToScreen(, )`.
```javascript JavaScript theme={null}
var screenName = "com.moengage.sampleapp.ui.activity.MainActivity";
// Optionally add data json object
var attributes = {
"nav_screen_attr1": "val1",
"nav_screen_attr2": 100,
"nav_screen_attr3": 123.11,
"nav_screen_attr4": true
};
moengage.navigateToScreen(value, attributes);
```
## Open Deeplink URL
To open a deeplink URL use `moengage.openDeepLink(, )`.
```javascript JavaScript theme={null}
var value = "moengage://testdeeplink/testActivity";
// Optionally add data json object
var attributes = {
"nav_screen_attr1": "val1",
"nav_screen_attr2": 100,
"nav_screen_attr3": 123.11,
"nav_screen_attr4": true
};
moengage.openDeepLink(value, attributes);
```
## Open Richlanding Screen
To open a Richlanding screen use `moengage.openRichLanding(, )`.
```javascript JavaScript theme={null}
var value = "https://www.google.com";
// Optionally add data json object
var attributes = {
"nav_screen_attr1": "val1",
"nav_screen_attr2": 100,
"nav_screen_attr3": 123.11,
"nav_screen_attr4": true
};
moengage.openRichLanding(value, attributes);
```
## Open Web URL
To open a URL in a web browser use `moengage.openWebURL(, )`.
```javascript JavaScript theme={null}
var value = "https://www.google.com";
// Optionally add data json object
var attributes = {
"nav_screen_attr1": "val1",
"nav_screen_attr2": 100,
"nav_screen_attr3": 123.11,
"nav_screen_attr4": true
};
moengage.openWebURL(value, attributes);
```
# Copy Text to Clipboard
To copy text to clipboard use `moengage.copyText(, )` API.
```javascript JavaScript theme={null}
moengage.copyText("COUPON100", "Rs.100 OFF!");
```
# Call
To perform call action use `moengage.call(` API.
```javascript JavaScript theme={null}
moengage.call("1234567890");
```
# SMS
To send an SMS use `moengage.sms(, )` API.
```javascript JavaScript theme={null}
moengage.sms("1234567890", "Hi There!");
```
# Share
To share a string content use `moengage.share()` API.
```javascript JavaScript theme={null}
moengage.share("Content to share");
```
# Custom Action
To perform custom action set the data using `moengage.customAction()` API.
```javascript JavaScript theme={null}
var attributes = {
"attr1": "val1",
"attr2": 100,
"attr3": 123.11,
"attr4": {
"int": 30
}
};
moengage.customAction(attributes);
```
# Data Tracking
The following sections will provide more details on the JS Methods for data tracking.
**JS Methods for Data Tracking**
You would need to integrate the JS Methods for Data Tracking in your template for the following use-cases:
* Tracking stats (clicks, close)
* Tracking events (for surveys, rating, lead gen)
* Tracking user attributes (for lead gen, signup etc.)
## Track Event
To track an event use `moengage.trackEvent(eventName, generalAttrJson, locationAttrJson, dateAttrJson, isNonInteractive, shouldAttachCampaignMeta)` API.
The trackEvent() takes in up to six parameters.
* eventName : String
* generalAttrJson : JSON Object for general attributes. Accepted data types are string, float, boolean, int, JSON objects.
```javascript JavaScript theme={null}
{
" ": " ",
" ": " ",
...
}
```
* locationAttrJson : JSON Object for location attributes. Accepts attribute name and latitude and longitude
```javascript JavaScript theme={null}
{
" ": {
"latitude": ,
"longitude":
},
" ": {
"latitude": ,
"longitude":
},
...
}
```
* dateAttrJson : JSON Object for date-time attributes. Accepts only ISO-8601 format dates
```javascript JavaScript theme={null}
{
" ": "yyyy-MM-dd'T'HH:mm:ss.fff'Z'",
" ": "yyyy-MM-dd'T'HH:mm:ss.fff'Z'",
...
}
```
* isNonInteractive: true if you want the event to be non-interactive, else false.
* shouldAttachMetaData: true if you want to attach campaign metadata to the event, else false.
Apart from the `eventName` parameter, other parameters are non-mandatory you can pass `{}`.\
It is important to maintain the order in which parameters are passed, i.e. if you want to pass only the location attributes general attribute should be passed `{}`.
For general cases, you would just be passing the event-name and event-attributes JSON and in this case you can skip the other params. A method for this case would be like - **`moengage.trackEvent("Response_Submitted", {"response1":"johndoe@gmail.com","response2":"approved"})`**
## Track Click Event
To track widget click events use `moengage.trackClick(widgetId)` API. The `` can be int or string, otherwise, the click event will not be tracked.
```javascript JavaScript theme={null}
//track click event
moengage.trackClick("image_1");
moengage.trackClick(10);
```
## Track Dismiss
To track in-app dismissal events, use `moengage.trackDismiss(widgetId)` API. The `` can be int or string. If not passed, the dismiss event will be tracked without widgetID. If not passed, the event will get tracked without ``
```javascript JavaScript theme={null}
moengage.trackDismiss("dismiss_btn_1");
moengage.trackDismiss(1);
```
## Track Dismiss(Deprecated - Use Above API with widget ID instead)
To track the in-app dismissal use `moengage.trackDismiss()` API.
```javascript JavaScript theme={null}
moengage.trackDismiss();
```
## Track Default User Attributes
The following JavaScript methods are available for tracking default user attributes.
| API name | API Description |
| ---------------------------- | ---------------------------------------------------------------------------- |
| moengage.setAlias() | Update user's unique id which was previously set by \`moengage.setUniqueId() |
| moengage.setUniqueId() | Set user UniqueId |
| moengage.setUserName() | Set user name |
| moengage.setFirstName() | Set user first name |
| moengage.setLastName() | Set user last name |
| moengage.setEmailId() | Set user email-id |
| moengage.setMobileNumber() | Set user mobile number |
| moengage.setGender() | Set user gender ("male"/"female"/"other") |
| moengage.setBirthDate() | Set user birthday NOTE: the value must be ISO-8601 format dates. |
| moengage.setUserLocation(, ) | Set user location |
| moengage.trackRating() | Track user input action rating |
Example:
```javascript JavaScript theme={null}
// Set alias
moengage.setAlias("user-alias");
// Set UniqueId
moengage.setUniqueId("unique-id");
// Set user name
moengage.setUserName("John Doe");
// Set user first name
moengage.setFirstName("Jane");
//Set user last name
moengage.setLastName("Doe");
// Set Email id
moengage.setEmailId("abc@abc.com");
// Set mobile number
moengage.setMobileNumber("1234567890");
// Set User gender
moengage.setGender("female");
// Set user birthday
moengage.setBirthDate("2017-08-02T06:05:30.000Z");
//Set user location
moengage.setUserLocation(3.0539652, 77.672683);
// Set rating value
moengage.trackRating(4.5);
```
## Track Custom user attributes
The following JavaScript methods are available for tracking custom user attributes.
| API name | API description |
| --------------------------------------- | ------------------------- |
| moengage.setUserAttribute(, ) | Set custom user attribute |
| moengage.setUserAttributeDate(, ) | Set custom user date |
| moengage.setUserAttributeLocation(, , ) | Set custom user location |
Example:
```javascript JavaScript theme={null}
// Set custom user attribute
moengage.setUserAttribute("int", 100);
moengage.setUserAttribute("float", 10.5);
moengage.setUserAttribute("String", "test");
moengage.setUserAttribute("Boolean", true);
//Set custom date attribute
moengage.setUserAttributeDate("Date1", "2017-08-02T06:05:30.000Z");
//Set custom user location
moengage.setUserAttributeLocation("Location 1", 3.0539652, 77.672683);
```
# JavaScript Bridge for On-Site Messaging and Landing Pages
Source: https://moengage.com/docs/developer-guide/components-for-sdk/javascript-bridge/javascript-bridge-for-on-site-messaging-and-landing-pages
Use the MoEngage JavaScript bridge to track events, clicks, dismissals, and form submissions from on-site messaging campaigns and landing pages.
On-Site Messaging (OSM) campaigns and Landing Pages support a JavaScript bridge interface that lets your web templates interact with the MoEngage SDK.
The bridges contain a set of APIs that you can access using the global variables `MoeOsm` for on-site messaging and `Moengage.landingPages` for landing pages.
The following sections list all the JS bridge methods available for OSM and landing pages, and explain each API in detail.
## On-site messaging (OSM)
The following table lists all the JS bridge methods available for on-site messaging campaigns.
| Method name | Description |
| ----------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `MoeOsm.trackEvent(eventName, eventAttr)` | Tracks a custom event from the OSM template. `eventName` is the name of the tracked event. `eventAttr` is the event attributes tracked along with the event. |
| `MoeOsm.trackClick(widgetId)` | Tracks clicks for campaign performance measurement. `widgetId` is the identifier of the widget being clicked. Helpful when multiple CTAs are present in a single template. |
| `MoeOsm.trackDismiss()` | Tracks dismiss stats for campaign performance measurement. |
| `MoeOsm.dismissMessage()` | Attach this method on your template to close the OSM message on a button click or any other click. |
**JS methods for OSM**
Integrate the JS methods below with your OSM template to execute on-click actions like message close, and to track data such as events, clicks, and dismissals.
### Track event
To track a custom event from your OSM template, use the `MoeOsm.trackEvent(eventName, eventAttr)` API.
```javascript JavaScript theme={null}
onclick="MoeOsm.trackEvent('add to cart', {price: 300})"
```
### Track click
To track widget click events, use the `MoeOsm.trackClick(widgetId)` API.
```javascript JavaScript theme={null}
onclick="MoeOsm.trackClick('1')"
```
### Track dismiss
To track dismiss events from your OSM template, use the `MoeOsm.trackDismiss()` API.
```javascript JavaScript theme={null}
MoeOsm.trackDismiss();
```
### Dismiss message
To dismiss the OSM message on a button click or any other click, use the `MoeOsm.dismissMessage()` API.
```javascript JavaScript theme={null}
MoeOsm.dismissMessage();
```
## Landing page
The following table lists all the JS bridge methods available for landing pages.
| Method name | Description |
| -------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------- |
| `Moengage.landingPages.trackClick(lp_context, )` | Tracks clicks on the landing page. All elements with a valid `href` have this click tracking added by default. |
| `Moengage.landingPages.trackFormSubmit(lp_context, )` | Tracks form submissions. This event is also tracked by default whenever a form element is present on the landing page. |
| `Moengage.landingPages.trackEvent(, lp_context, )` | Tracks a custom event from the landing page. |
**JS methods for landing pages**
Integrate the JS methods below with your landing page template to track clicks on page elements, capture form submissions, and track custom events.
### The lp\_context Object
`lp_context` is the landing page context object that MoEngage makes available globally on every landing page. Pass it to the bridge methods as-is, without modifying it or creating it yourself. MoEngage uses this object to enrich each tracked event with the full landing page context, including the `moe_lp_formatted_id` attribute that campaign statistics use to attribute the event to your landing page.
Always pass the `lp_context` object as-is. Do not build the context argument yourself, or the event will be missing `moe_lp_formatted_id` — it still appears on the user profile, but the click is not counted in the landing page campaign statistics.
✅ **Correct:** `Moengage.landingPages.trackClick(lp_context, 'Register_Now')`
❌ **Incorrect:** `Moengage.landingPages.trackClick({id: , name: }, 'Register_Now')`
### Track click
To track clicks on elements within your landing page, use the `Moengage.landingPages.trackClick(lp_context, )` API.
All elements with a valid `href` attribute have this click tracking added by default.
```javascript JavaScript theme={null}
onclick="Moengage.landingPages.trackClick(lp_context, 'Register_Now')"
```
### Track form submit
To track form submissions on your landing page, use the `Moengage.landingPages.trackFormSubmit(lp_context, )` API.
This event is tracked by default whenever a form element is present on the landing page.
```javascript JavaScript theme={null}
const attributeObj = {
name: document.getElementById("name").value.trim(),
email: document.getElementById("email").value.trim(),
mobile: document.getElementById("mobile").value.trim(),
consent: document.getElementById("consent").checked ? "yes" : "no"
};
Moengage.landingPages.trackFormSubmit(lp_context, attributeObj);
```
### Track event
To track a custom event from your landing page, use the `Moengage.landingPages.trackEvent(, lp_context, )` API.
```javascript JavaScript theme={null}
Moengage.landingPages.trackEvent('plan_selected', lp_context, {plan: 'premium', price: 499});
```
# Android Push Configuration For Hybrid Applications
Source: https://moengage.com/docs/developer-guide/components-for-sdk/push-notification/android-push-configuration-for-hybrid-applications
Configure Android push notification metadata and FCM authentication for your hybrid MoEngage app.
# Configuring your MoEngage Account
* Please make sure you have set up [Firebase](https://firebase.google.com/docs/android/setup) in your application.
* Configure FCM Authorization on the MoEngage Dashboard. For more information, refer to [FCM Authentication](/docs/developer-guide/android-sdk/push/basic/fcm-authentication).
* Ensure you add the keys in both the Test and Live environments.
## Adding metadata for push notification
Metadata regarding the notification is required to show push notifications where the small icon and large icon drawable are mandatory.
For more information about API reference for all the possible options, refer to [NotificationConfig](https://moengage.github.io/android-api-reference/core/com.moengage.core.config/-notification-config/index.html).
Use the [*configureNotificationMetaData()*](https://moengage.github.io/android-api-reference/core/com.moengage.core/-mo-engage/-builder/index.html) to transfer the configuration to the SDK.
```Kotlin Kotlin theme={null}
import com.moengage.core.DataCenter
import com.moengage.core.MoEngage
val moEngage = MoEngage.Builder(this, "YOUR_WORKSPACE_ID", [YOUR_DATA_CENTER])
.configureNotificationMetaData(NotificationConfig(R.drawable.small_icon, R.drawable.large_icon))
MoEInitializer.initializeDefaultInstance(applicationContext, moEngage)
```
```Java Java theme={null}
import com.moengage.core.DataCenter;
import com.moengage.core.MoEngage;
MoEngage.Builder moEngage = new MoEngage.Builder(this, "YOUR_WORKSPACE_ID", [YOUR_DATA_CENTER])
.configureNotificationMetaData(new NotificationConfig(R.drawable.small_icon, R.drawable.large_icon));
MoEInitializer.INSTANCE.initializeDefaultInstance(getApplicationContext(), moEngage);
```
Could you make sure that the SDK is initialized with the metadata in the onCreate() of the Application class for push notifications to work?
**Notification Small Icon Guidelines**
The notification small icon should be flat, pictured face on, and must be white on a transparent background.
## Notification Small Icon Density, Size
| Density (dp) | Size (px) |
| ------------ | --------- |
| MDPI | 24x24 |
| HDPI | 36x36 |
| XHDPI | 48x48 |
| XXHDPI | 72x72 |
| XXXHDPI | 96x96 |
**Critical**
Please ensure the small icon is set. If the small icon is not set, notifications will not be displayed.
# Real-time Uninstall Tracking
Source: https://moengage.com/docs/developer-guide/components-for-sdk/tracking/real-time-uninstall-tracking
Track app uninstalls in real time using Firebase Cloud Functions instead of daily silent push notifications.
# What is real-time uninstall tracking?
Real-time uninstall tracking enables you to track the uninstall immediately after the user uninstalls an app integrated with *Firebase Analytics*.
The Firebase tracks the event **app-remove** when an app is uninstalled. The **app-remove** event is used by MoEngage using Firebase Cloud Functions.
# What are Firebase Cloud Functions?
Cloud Functions for Firebase is a serverless framework that programmatically provides responses to events triggered by Firebase features and HTTPS requests. For more information, refer to [Cloud Functions for Firebase](https://firebase.google.com/docs/functions).
# How is real-time uninstall tracking different from the existing uninstall tracking?
The current uninstall tracking in MoEngage happens by sending Silent Push notifications once a day to all user devices. Up to 24 hours is required to track uninstall.
Using the real-time uninstall tracking, the uninstall is tracked immediately by the Firebase SDK so that you can respond quickly to user uninstalls
# Implementation of real-time uninstall tracking
Ensure you have the Firebase Blaze plan account.
To deploy Cloud Functions to the runtime environment, your project must be on the [Firebase pricing plan](https://firebase.google.com/pricing).
## Set up a common identifier
Ensure your app has integrated Firebase Analytics SDK. For more information, refer to [Get started with Google Analytics](https://firebase.google.com/docs/analytics/get-started).
Add the following code in your app to set up a common identifier between MoEngage and Firebase.
```kotlin Kotlin theme={null}
val firebaseAnalytics = FirebaseAnalytics.getInstance(this)
MoEHelper.getInstance(this).setUniqueId("")
firebaseAnalytics.setUserProperty("MOE_USER_ATTRIBUTE_UNIQUE_ID", "")
}
```
**Note:** The following values are not allowed in the UniqueID field: "unknown", "guest", "null", "0", "1", "true", "false", "user\_attribute\_unique\_id", "(empty)", "na", "n/a", "", "dummy\_seller\_code", "user\_id", "id", "customer\_id", "uid", "userid", "none", "-2", "-1", "2"
## Set up the conversion event using Firebase
Ensure that the App is integrated with Firebase Analytics for real-time uninstall tracking.
Firebase analytics automatically collects the event `app_remove`. The `app_remove` event is, an Android-only event, tracked when an application package is removed or uninstalled from the device, regardless of the installation source. To set up a real-time uninstall make sure that the `app_remove` event is marked as the conversion event on the Firebase dashboard.
To set up a conversion event, follow these steps:
1. Navigate to [Firebase Console](https://console.firebase.google.com/) and select the Firebase project integrated with the app.
2. From the Firebase dashboard, select **Analytics > Events**.
3. Enable the ***Mark as Conversion*** toggle for **app\_remove** event in the event list.
## Create Cloud Function
After the conversion event is set up, use the [Cloud Function for Firebase](https://firebase.google.com/docs/functions/get-started) to create a function and send the **app\_remove** event to MoEngage.
To create and publish a cloud function using Node JS, follow these steps:
1. Install [Node.js](https://nodejs.org/en/) and [npm](https://www.npmjs.com/).\
Node.js environment is required to write functions. Use the Firebase CLI to deploy functions to the Cloud Functions runtime.
2. Install Firebase CLI using the following code:
```CLI CLI Command theme={null}
npm install -g firebase-tools
```
3. Run ***firebase login*** to log in using the browser and authenticate the firebase tool.
4. Navigate to your Firebase project directory.
5. Run ***firebase init functions***.
6. Select ***Javascript*** as a language option.
7. Add the following code to the **index.js** file:
```javascript index.js theme={null}
"use strict";
const functions = require("firebase-functions");
const admin = require("firebase-admin");
const https = require("https");
require("firebase-functions/lib/logger/compat");
admin.initializeApp();
exports.sendAndroidUninstallToMoEngage = functions.analytics.event("app_remove")
.onLog((event) => {
console.log("sendAndroidUninstallToMoEngage() : Event is: " +
JSON.stringify(event));
return exports.sendAppUninstallEventData(event);
});
exports.sendAppUninstallEventData = function(event) {
//fetch the unique ID
var uniqueId = event.user.userProperties.MOE_USER_ATTRIBUTE_UNIQUE_ID.value;
//send event using S2S API of MoEngage
//https://www.moengage.com/docs/api/data/data-overview
};
```
8. Add the following code to **package.json** file:
```json pacakage.json theme={null}
{
"name": "functions",
"description": "Cloud Functions for Firebase",
"scripts": {
"lint": "eslint .",
"serve": "firebase emulators:start --only functions",
"shell": "firebase functions:shell",
"start": "npm run shell",
"deploy": "firebase deploy --only functions",
"logs": "firebase functions:log"
},
"engines": {
"node": "12"
},
"main": "index.js",
"dependencies": {
"firebase-admin": "^10.0.0",
"firebase-functions": "^3.16.0",
"firebase-tools": "^9.23.0",
"g": "^2.0.1",
"requestretry": "^4.1.1"
},
"devDependencies": {
"eslint": "^7.6.0",
"eslint-config-google": "^0.14.0",
"eslint-plugin-promise": "^4.0.1",
"firebase-functions-test": "^0.2.0",
"requestretry": "^4.1.1",
"web-push": "^3.4.5"
},
"private": true
}
```
# Connect Your IDE to MoEngage Docs
Source: https://moengage.com/docs/developer-guide/connect-your-ide-to-moengage-docs
Connect your IDE to the MoEngage docs MCP server so your AI assistant can reference current SDK documentation while you integrate.
Connect your IDE to the MoEngage docs MCP server to provide your AI assistant with direct, on-demand access to this documentation site. The Model Context Protocol (MCP) is a standard that enables AI tools to fetch and search documentation. The assistant can then retrieve exact method signatures, parameters, and implementation steps for the SDK version you are integrating, and generate code grounded in current MoEngage documentation.
This page covers the **docs MCP** (`https://www.moengage.com/docs/mcp`), which gives an AI assistant read access to this documentation site. It is separate from the [MoEngage MCP Server and Connector](/docs/user-guide/ai-and-intelligence/merlin-ai/moengage-mcp-server) (`https://mcp.moengage.com`), which connects an assistant to your MoEngage workspace to build campaigns, manage segments, and analyze performance.
## Connect Your IDE to the Docs MCP Server
Use this URL wherever your IDE or client asks for a remote MCP server or custom connector:
```text theme={null}
https://www.moengage.com/docs/mcp
```
Last verified: July 2026
1. Open **Cursor Settings** → **Tools & Integrations** → **MCP Tools**.
2. Click **Add Custom MCP**.
3. Add an entry to `mcp.json`:
```json theme={null}
{
"mcpServers": {
"moengage-docs": {
"url": "https://www.moengage.com/docs/mcp"
}
}
}
```
Requires **VS Code 1.99 or later** (remote MCP support) and Copilot **agent mode**. Last verified: July 2026.
1. Open the Command Palette and run **MCP: Add Server** (or edit `.vscode/mcp.json` directly).
2. Choose **HTTP (remote server)** and enter the URL: `https://www.moengage.com/docs/mcp`.
3. Save, then open **Copilot Chat** and switch to **Agent** mode. The MoEngage docs tools appear in the tools picker.
For setup steps for other clients — such as Claude Code and Claude Desktop — and a full list of the tools the docs MCP server exposes, see [Documentation Access to Agents](/docs/documentation-access-to-agents).
The docs MCP server is read-only — it can search this documentation site by keyword and retrieve the content of specific pages. It does not access your MoEngage workspace, campaigns, or account data, and no authentication is required to connect.
Prompts and retrieved documentation pass through your AI provider's cloud (for example, GitHub's for Copilot, or the model provider configured in Cursor). Review your provider's data-handling policy before sending proprietary code alongside your prompts.
### Confirm the Connection
Submit a documentation query to your assistant and verify it retrieves the information from the connector rather than relying on training data. For example:
```text theme={null}
Search the MoEngage docs for how to initialize the Android SDK, and tell me which page you found it on.
```
If the assistant cites a MoEngage documentation URL in its response, the connection is functioning correctly.
## Writing Prompts for SDK Development
Without the docs MCP connected, an assistant answers SDK questions from training data, which can suggest deprecated methods or the wrong platform's syntax. Naming the SDK, platform, and specific task in your prompt gives a connected assistant the context it needs to search for and cite the right page before generating code. Replace the bracketed placeholders with your own details.
### Android SDK
```text theme={null}
Using the MoEngage Android SDK, [initialize the SDK with my Workspace ID and data center / track a custom event called "" with properties / set up FCM push notifications].
```
### iOS SDK
```text theme={null}
Using the MoEngage iOS SDK, [initialize the SDK with my Workspace ID and data center / track a custom event called "" with properties / configure APNs push notifications].
```
### Web SDK
```text theme={null}
Using the MoEngage Web SDK, [initialize the SDK on my website using the CDN method / track a custom event called "" / register for web push notifications].
```
### React Native SDK
```text theme={null}
Using the MoEngage React Native SDK, [link native dependencies for Android and iOS / initialize the SDK / subscribe to in-app message callbacks].
```
### Flutter SDK
```text theme={null}
Using the MoEngage Flutter SDK, [add moengage_flutter to my pubspec.yaml and initialize it / track a custom event called "" with attributes].
```
## Plain-Text Documentation
MoEngage also publishes this documentation in the [llms.txt](https://llmstxt.org/) format, giving assistants and tools that read plain text an alternative to connecting through MCP.
| File | Contents |
| ------------------------------------------------------------ | -------------------------------------------------------------------------------------------------------------- |
| [llms.txt](https://www.moengage.com/docs/llms.txt) | An index of documentation pages with short descriptions, for assistants that fetch individual pages on demand. |
| [llms-full.txt](https://www.moengage.com/docs/llms-full.txt) | The full documentation content in a single file, for assistants that read the complete corpus at once. |
Insert the file contents into your assistant's context, or configure a tool that supports `llms.txt` to reference the URL directly.
## Troubleshooting
Confirm the connector was saved with the exact URL `https://www.moengage.com/docs/mcp` and that your IDE is configured for HTTP (not stdio) transport. Restart your IDE — most clients only load newly added MCP servers on restart.
Some clients require you to explicitly enable a connector's tools for a given chat, or to enable **Agent** mode (Copilot) before invoking them. Check your client's tools or connectors menu for the MoEngage docs entry and verify it is enabled.
Ask it to search again with more specific terms — for example, naming the exact SDK method or platform. If a page still looks wrong or out of date, use [Suggest a Feature](/docs/user-guide/contact-support/suggest-a-feature) or the docs MCP server's feedback tool to report it.
# Compliance
Source: https://moengage.com/docs/developer-guide/cordova-sdk/compliance/compliance
Opt out of data tracking and enable or disable the MoEngage Cordova SDK from the JavaScript layer.
Use the APIs below to control what the MoEngage SDK tracks, based on the consent a user has given.
## Enable or Disable Data Tracking
To stop the SDK from tracking custom events and user attributes, opt out of data tracking. The SDK rejects all events and user attributes until you opt back in. Data tracking is enabled by default.
```javascript JavaScript theme={null}
var moe = MoECordova.init(YOUR_WORKSPACE_ID);
moe.optOutDataTracking(true); // Stop tracking events and user attributes
moe.optOutDataTracking(false); // Resume tracking events and user attributes
```
## Enable or Disable the SDK
To stop the SDK from tracking any user information or sending any data to MoEngage, call `disableSdk()`.
```javascript JavaScript theme={null}
var moe = MoECordova.init(YOUR_WORKSPACE_ID);
moe.disableSdk();
```
All SDK APIs are non-operational until you call `enableSdk()`. The SDK is enabled by default, so call `enableSdk()` only if you disabled it earlier.
```javascript JavaScript theme={null}
var moe = MoECordova.init(YOUR_WORKSPACE_ID);
moe.enableSdk();
```
## Delete User Data
To delete the current user's profile from the MoEngage server, refer to [Delete User From MoEngage Server](/docs/developer-guide/cordova-sdk/data-tracking/delete-user-from-moengage-server).
# Delete User From MoEngage Server
Source: https://moengage.com/docs/developer-guide/cordova-sdk/data-tracking/delete-user-from-moengage-server
Delete the current user from the MoEngage server using the Cordova SDK on Android.
This API is supported from **cordova-moengage-core** version **8.4.0** and is only available for the Android platform and is a no-operation for other platforms.
To delete the current user from the MoEngage server use ***deleteUser()*** method as shown below
```javascript JavaScript theme={null}
var moe = MoECordova.init(YOUR_WORKSPACE_ID);
moe.deleteUser().then(result =>
//add your code to handle the callback
console.log("User Deletion Result",result.toString())
).catch(error =>
//add your code to handle the Error
console.error("Error while Deleting User",error.toString())
);
```
# Enable Advertising Identifier Tracking
Source: https://moengage.com/docs/developer-guide/cordova-sdk/data-tracking/enable-advertising-identifier-tracking
Enable advertising identifier tracking in your Cordova app for accurate device analytics.
For accurate analytics around devices and tracking Re-installs, it is essential to track the Advertising Identifier.
This is not supported in iOS
## Add Ad Identifier Library
Add the below dependency in the application level ***build-extras.gradle*** file.
```groovy Groovy theme={null}
implementation("com.google.android.gms:play-services-ads-identifier:18.0.1")
```
To enable Advertising Identifier tracking use the *enableAdIdTracking()* method as shown below.
```javascript Javascript theme={null}
var moe = MoECordova.init(YOUR_WORKSPACE_ID);
moe.enableAdIdTracking();
```
Before you enable Advertising Id tracking please ensure the application is complying with the [Google Play Policy](https://support.google.com/googleplay/android-developer/answer/10144311) regarding Advertising Id tracking. Refer to our [help document](https://www.moengage.com/docs/user-guide/data/privacy/android-advertising-id-tracking) for more information on the policy.
In case, you need to disable advertising-id after enabling tracking use the following method.
```javascript Javascript theme={null}
var moe = MoECordova.init(YOUR_WORKSPACE_ID);
moe.disableAdIdTracking();
```
The above APIs are available only starting plugin version 7.3.3. In the older versions, Advertising Identifier tracking is enabled by default.
# Install/Update differentiation
Source: https://moengage.com/docs/developer-guide/cordova-sdk/data-tracking/install-update-differentiation
Set the app status as install or update in the MoEngage Cordova SDK for migration tracking.
This is solely required for migration to the MoEngage Platform. We need your help to tell the SDK whether the user is a new user for on your app(first Install) or an existing user who has updated to the latest version.
Make use of the **setAppStatus** method as shown below to track Install/Update as shown below:
```javascript JavaScript theme={null}
var moe = MoECordova.init(YOUR_WORKSPACE_ID);
// For fresh Install
moe.setAppStatus("INSTALL");
// For tracking App Update
moe.setAppStatus("UPDATE");
```
# Tracking Events
Source: https://moengage.com/docs/developer-guide/cordova-sdk/data-tracking/tracking-events
Track user actions and event attributes using the MoEngage Cordova SDK for segmentation and campaigns.
SDK adheres to the MoEngage FUP policies. For more information, refer to the [Fair Usage Policy](https://www.moengage.com/docs/user-guide/data/key-concepts/fair-usage-policy-fup).
Tracking events is how you record any actions your users perform, along with any properties that describe the action.
```javascript JavaScript theme={null}
var moe = MoECordova.init(YOUR_WORKSPACE_ID);
moe.trackEvent(eventName, generalAttributes, locationAttributes, dateTimeAttributes, isNonInteractive);
```
The trackEvent() takes in 5 parameters eventName and eventAttribute.
* eventName : String
* generalAttributes : JSON Object for general attributes. Accepted data types are string, number, boolean
```json JSON theme={null}
{
"": "",
"": "",
"": "",
...
}
```
* locationAttributes: JSON Object for location attributes. Accepts attribute name and latitude and longitude
```json JSON theme={null}
{
"": {
"latitude": ,
"longitude":
},
"": {
"latitude": ,
"longitude":
},
"": {
"latitude": ,
"longitude":
},
...
}
```
* dateTimeAttributes: JSON Object for date-time attributes. Accepts only ISO-8601 format dates
```json JSON theme={null}
{
"": "yyyy-MM-dd'T'HH:mm:ss.fff'Z'",
"": "yyyy-MM-dd'T'HH:mm:ss.fff'Z'",
"": "yyyy-MM-dd'T'HH:mm:ss.fff'Z'",
...
}
```
* isNonInteractive: true if you want the event to be non-interactive, else false.
Apart from the **eventName** parameter, other parameters are non-mandatory you can choose to pass **null** or or **undefined**.\
It is important to maintain the order in which parameters are passed, i.e. if you want to pass only the location attributes general attribute should be passed **null** or or **undefined**
Example
```json JSON theme={null}
moe.trackEvent("testEvent", {"attr1": "string", "attr2": 123, "attr3": false},{"loc1": { "latitude": 14.90123,"longitude": 13.1627} },
{"date1" : "2017-08-02T06:05:30.000Z"}, true);
```
You can not use "moe\_" as a prefix while naming events, event attributes, or user attributes. It is a system prefix and using it might result in periodic blacklisting without prior communication.
# Analytics
MoEngage SDK has started tracking user session and application traffic source. To learn more about how user session and application traffic source tracking works, refer to the following docs:
* [Session and Source Analysis](https://www.moengage.com/docs/user-guide/analyze/analytics/session-and-source/session-and-source-analysis)
* [Advanced Session and Source Analysis](https://www.moengage.com/docs/user-guide/analyze/analytics/session-and-source/advanced-session-and-source-analysis)
With user session tracking we have introduced the flexibility to selectively mark events as non-interactive.
## What is a non-interactive event?
Events that do not affect the session calculation in anyways are called non-interactive events. Non-interactive events have the below properties
* Do not start a new session.
* Do not extend the session.
* Do not have information related to a user session.
# Tracking User Attributes and User Identity
Source: https://moengage.com/docs/developer-guide/cordova-sdk/data-tracking/tracking-user-attributes
Track user attributes and manage login and logout states using the MoEngage Cordova SDK.
## Identity Management
Setting identifiers is important to:
* Tie user behavior across platforms.
* Ensure unnecessary or stale users are not created.
* Identify users across installs and re-installs.
### Login with a Single Identifier
Call the API below to pass the identifier to the MoEngage SDK.
```javascript JavaScript theme={null}
var moe = MoECordova.init(YOUR_WORKSPACE_ID);
moe.identifyUser("identifier");
```
* This method replaces the deprecated `setUniqueId()` and `setAlias()`. If you are using either of these methods, replace them with `identifyUser()`.
* The following values are not allowed in the identifier field: `unknown`, `guest`, `null`, `0`, `1`, `true`, `false`, `user_attribute_unique_id`, `(empty)`, `na`, `n/a`, `""`, `dummy_seller_code`, `user_id`, `id`, `customer_id`, `uid`, `userid`, `none`, `-2`, `-1`, `2`.
### Login with Multiple Identifiers
If your application has multiple identifiers for a user, pass all identifiers to the SDK using the API below.
```javascript JavaScript theme={null}
var moe = MoECordova.init(YOUR_WORKSPACE_ID);
moe.identifyUser({ "identifierName1": "identifierValue1", "identifierName2": "identifierValue2" });
```
Use the standard identifier keys below when passing common attributes as identifiers:
| User attribute | Key |
| :------------- | :----- |
| ID | `uid` |
| Email | `u_em` |
| Gender | `u_gd` |
| Birthday | `u_bd` |
| Name | `u_n` |
| First name | `u_fn` |
| Last name | `u_ln` |
| Mobile number | `u_mb` |
For custom identifiers, use any key name that is not in the reserved keywords list.
**Behavior of multiple `identifyUser()` calls:**
* If you call `identifyUser()` without logging out first, the existing logged-in user's identifiers are updated.
* If you call `identifyUser()` multiple times with different identifier names, the SDK appends the new identifier to the already set identifiers. Refer to the [Identity resolution documentation](/docs/user-guide/data/user-data/unified-identity-identity-resolution) to learn more.
* For workspaces with Identity resolution enabled, the SDK stores previous identifier values and detects changes when `identifyUser()` is called with new values.
If a network issue prevents the identifier from reaching the MoEngage server, the SDK caches the `identifyUser()` call and retries automatically once the network is available or the next time the app opens.
To enable or disable the SDK and data tracking, refer to [Compliance](/docs/developer-guide/cordova-sdk/compliance/compliance).
**Forced Logout:** The MoEngage SDK no longer automatically merges or logs out the previous user when a new user is detected on the device. Call `logout()` explicitly before identifying a new user to avoid data corruption.
### Logout
The application needs to notify the MoEngage SDK whenever the user is logged out of the application. Call the API whenever the user is logged out of the application to notify the SDK.
```javascript JavaScript theme={null}
var moe = MoECordova.init(YOUR_WORKSPACE_ID);
moe.logout();
```
Logout clears the data and identity of the current user on the device and resets the SDK state. Call `identifyUser()` again when the next user logs in. For how identifiers map to user profiles, refer to [Identity resolution](/docs/user-guide/data/user-data/unified-identity-identity-resolution).
#### Logout Callback Listener
Clearing user data and resetting the SDK state is an asynchronous process. Wait for the SDK to complete logout before navigating the user away or clearing your app's local state.
Register a listener for the `onLogoutComplete` event to detect successful logout.
```javascript JavaScript theme={null}
var moe = MoECordova.init(YOUR_WORKSPACE_ID);
moe.on('onLogoutComplete', function (payloadInfo) {
console.log("Received callback 'onLogoutComplete', data: " + JSON.stringify(payloadInfo));
});
```
Minimum plugin version required: Core 10.1.0.
#### Logout Callback Data
The `onLogoutComplete` callback receives the following payload:
```json JSON theme={null}
{
"type": "MoELogoutComplete",
"accountMeta": {
"appId": ""
},
"platform": "android/ios"
}
```
**type** - Event type that triggered the callback. Always `MoELogoutComplete` for logout.\
**accountMeta.appId** - The Workspace ID of MoEngage.\
**platform** - Native platform from which the callback is triggered. Possible values - **android**, **ios**
## SDK User Attributes Keys
You can also set the default user attributes like mobile number, gender, user name, birthday, location, etc using the below APIs
```javascript JavaScript theme={null}
var moe = MoECordova.init(YOUR_WORKSPACE_ID);
moe.setUserName("abc");
moe.setFirstName("abc");
moe.setLastName("xyz");
moe.setEmail("abc@xyz.com");
moe.setPhoneNumber(1234567890);
moe.setGender("Male"); // OR Female
moe.setLocation(25.23, 73.23);
// Format - ISO-8601 String
moe.setBirthdate("1970-01-01T12:00:00Z");
```
For setting custom user attributes, use the method **setUserAttribute(key, value).**
```javascript JavaScript theme={null}
var moe = new MoECordova.init(YOUR_WORKSPACE_ID);
moe.setUserAttribute("", "");
```
Date attributes and Birthday in ISO-8601 format -**yyyy-MM-dd'T'HH:mm:ss'Z'**
```javascript JavaScript theme={null}
var moe = MoECordova.init(YOUR_WORKSPACE_ID);
moe.setUserAttributeLocation("attribute", 25.23, 73.23);
```
You can use the following method set the timestamp user attribute :
```javascript JavaScript theme={null}
var moe = MoECordova.init(YOUR_WORKSPACE_ID);
moe.setUserAttributeISODateString("LastPurchaseDate", "1970-01-01T12:00:00Z");
```
You can not use "moe\_" as a prefix while naming events, event attributes, or user attributes. It is a system prefix and using it might result in periodic blacklisting without prior communication.
### Custom Boolean User Attribute
#### iOS(optional)
Starting from version 9.x.x of cordova-moengage-core, the default tracking for the custom boolean attribute will be changed to ***boolean(true/false)*** from ***double(0/1)***. To configure this, use ***analyticsConfig*** with ***shouldTrackUserAttributeBooleanAsNumber*** and pass true to track the boolean as double. By default, this is set as ***false*** to track boolean as true/false
Refer to the initialization code snippet below.
```java TypeScript theme={null}
let initializationConfig = {analyticsConfig: {shouldTrackUserAttributeBooleanAsNumber: true}};
var moe = MoECordova.init(YOUR_WORKSPACE_ID, initializationConfig);
```
Refer to the example code below for tracking the boolean user attribute
```java TypeScript theme={null}
var moe = new MoECordova.init(YOUR_WORKSPACE_ID);
/// If shouldTrackUserAttributeBooleanAsNumber is passed as true then `boolean attribute True` will tracked with value 1 else true
moe.setUserAttribute("boolean attribute True", true);
/// If shouldTrackUserAttributeBooleanAsNumber is passed as true then `boolean attribute False` will tracked with value 0 else false
moe.setUserAttribute("boolean attribute False", false);
```
### Reserved keywords for User Attributes
Below is the list of keys that should not be used when tracking user attributes.
* USER\_ATTRIBUTE\_UNIQUE\_ID
* USER\_ATTRIBUTE\_USER\_EMAIL
* USER\_ATTRIBUTE\_USER\_MOBILE
* USER\_ATTRIBUTE\_USER\_NAME
* USER\_ATTRIBUTE\_USER\_GENDER
* USER\_ATTRIBUTE\_USER\_FIRST\_NAME
* USER\_ATTRIBUTE\_USER\_LAST\_NAME
* USER\_ATTRIBUTE\_USER\_BDAY
* USER\_ATTRIBUTE\_NOTIFICATION\_PREF
* USER\_ATTRIBUTE\_OLD\_ID
* MOE\_TIME\_FORMAT
* MOE\_TIME\_TIMEZONE
* USER\_ATTRIBUTE\_DND\_START\_TIME
* USER\_ATTRIBUTE\_DND\_END\_TIME
* MOE\_GAID
* INSTALL
* UPDATE
* MOE\_ISLAT
* status
* user\_id
* source
# Cordova SDK Deprecated Versions
Source: https://moengage.com/docs/developer-guide/cordova-sdk/deprecated-versions/deprecated-versions
Check the support status of MoEngage Cordova SDK major versions: which are current, which are still supported, and which are deprecated, along with deprecation dates.
Use this page to check the support status of MoEngage Cordova SDK major versions. It lists which versions are current, which are still supported, and which are deprecated, along with deprecation dates. This is the source of truth for Cordova SDK version status. For how the lifecycle works and what deprecation means for your app, see the [SDK Deprecation Policy](/docs/developer-guide/sdk-lifecycle-and-policies/sdk-deprecation-policy). To set up the Cordova SDK, see the [integration guide](/docs/developer-guide/cordova-sdk/sdk-integration/sdk-installation/framework-dependency).
## Version Support Status
**Current** — The latest major version. It receives new features, fixes, and support.
**Supported** — An older major version within its 3-year support window. It continues to receive support.
**Deprecated** — A version outside its support window. It no longer receives fixes or support.
| Major Version | Status | Deprecation Date | Notes |
| -------------------- | ---------- | ---------------- | -------------------------------------------------------------------------------------------------------------- |
| Core 10.x | Current | TBD | Latest major version. Receives new features, fixes, and support. |
| Core 9.x | Supported | TBD | Receives support. |
| Core 8.7.0 and below | Deprecated | August 2027 | Support, debugging, and fixes are no longer available. Data continues to flow. Upgrade to the current version. |
MoEngage supports each major version for 3 years from its initial release; deprecation then takes effect the following August. See the [SDK Deprecation Policy — Support Window](/docs/developer-guide/sdk-lifecycle-and-policies/sdk-deprecation-policy#support-window) for details. This table is updated when MoEngage announces further deprecations. To receive advance notice, subscribe to the SDK newsletter and ensure your workspace administrator email is current.
## Understanding Deprecated Status
A deprecated version continues to send and receive data. MoEngage does not block traffic. After the deprecation date, MoEngage no longer debugs, fixes, or supports issues specific to that version, and the resolution for any issue is to upgrade to the current version. All fixes ship in the current version only and are not available on supported or deprecated versions. For the complete policy, see the [SDK Deprecation Policy](/docs/developer-guide/sdk-lifecycle-and-policies/sdk-deprecation-policy).
## Upgrading to a Supported Version
* Refer to the [Cordova SDK release notes](/docs/release-notes/sdks/cordova) for the current major version changes.
* Review the [Cordova SDK migration guides](/docs/developer-guide/cordova-sdk/migration/migrating-to-4xx) for integration changes.
* For help, contact your MoEngage Customer Success Manager (CSM) or the Support team.
## Frequently Asked Questions
Apps on deprecated versions continue to run and data continues to flow. However, MoEngage no longer provides support, debugging, or fixes for deprecated versions. Upgrading to the current or a supported version using the [Cordova SDK release notes](/docs/release-notes/sdks/cordova) ensures you receive continued support.
MoEngage sends advance notice 6 to 12 months before the deprecation date through email to workspace administrators, the quarterly SDK newsletter, in-dashboard notices, and SDK release notes. See the [SDK Deprecation Policy](/docs/developer-guide/sdk-lifecycle-and-policies/sdk-deprecation-policy) for the full notification schedule.
# InApp NATIV
Source: https://moengage.com/docs/developer-guide/cordova-sdk/in-app-messages/inapp-nativ
Set up MoEngage in-app NATIV campaigns in your Cordova app to show contextual messages to users.
In-App Messaging is custom views that you can send to a segment of users to show custom messages or give new offers or take to some specific pages. They can be created from your MoEngage account.
## Install Android Dependency
## Install using BOM
Integration using BOM is the recommended way of integration; refer to the [Install Using BOM ](/docs/developer-guide/android-sdk/sdk-integration/basic-integration/Install-Using-BOM)document. Once you have configured the BOM add the dependency in the *app/build.gradle* file as shown below:
```json build.gradle wrap theme={null}
dependencies {
...
implementation("com.moengage:inapp")
}
```
Once the BOM is configured, include the specific MoEngage modules required for the application. \
Note: Version numbers are not required for these dependencies; the BOM automatically manages them.
## Manual installation

Add the following dependency in the \*\*android/\*\****app/build-extras.gradle*** file.
```json build-extras.gradle theme={null}
dependencies {
...
implementation("com.moengage:inapp:\$sdkVersion")
}
```
replace **\$sdkVersion** with the appropriate SDK version
### **Requirements for displaying images and GIFs in InApp**
Starting InApp version **7.0.0,** SDK requires [Glide](https://bumptech.github.io/glide/) to show images and GIFs in the in-apps. You need to add the below dependency in your **build.gradle** file.
```groovy Groovy theme={null}
dependencies {
...
implementation("com.github.bumptech.glide:glide:4.16.0")
}
```
# How to show In-App Messages?
MoEngage refreshes the list of eligible In-App campaigns via a meta API call. During an active session, the list refreshes when the app returns from the background to the foreground (at most once every 15 minutes), and immediately when a new session starts or the app is killed and relaunched.
Use **showInApp()** method to show inApp as shown below:
```javascript JavaScript theme={null}
var moe = MoECordova.init(YOUR_WORKSPACE_ID);
moe.showInApp();
```
# Nudges
Nudges are non-intrusive In-App messages that can be displayed at various positions on the screen. Use the **showNudge()** method to display them:
```javascript JavaScript theme={null}
var moe = MoECordova.init(YOUR_WORKSPACE_ID);
// To show a nudge with the Top position
moe.showNudge(MoENudgePosition.top);
// To show a nudge with the Bottom position
moe.showNudge(MoENudgePosition.bottom);
// To show a nudge with the BottomLeft position
moe.showNudge(MoENudgePosition.bottomLeft);
// To show a nudge with the BottomRight position
moe.showNudge(MoENudgePosition.bottomRight);
// To show a nudge with the Any position
moe.showNudge(MoENudgePosition.any);
```
# InApp/Nudge Redirection default behavior
On clicking an Inapp or Nudge, MoEngage SDKs will handle **only rich landing navigation** redirection.
For the screen name and deep link redirection, your app code should redirect the user to the right screen or deep link. To handle the screen name and deep link redirection, you must implement inapp click callback methods. MoEngage SDK will just pass the inapp payload to this call back code. Implementation steps are mentioned in the InApp callback section of the Integration.
# Context-Based InApps
We have introduced context-based InApps. While creating InApp campaigns you can set the contexts OR tags to the campaign. SDK will check with the current context set in the App and show the inApp only when a current set context matches the campaign context.
## Set Current Context:
To set the current context for the InApp module use **setCurrentContext()** method as shown below:
```javascript JavaScript theme={null}
moe.setCurrentContext(\["Home","CategoriesScreen"\]);
```
## Reset Context:
To reset the current context for the InApp module call **resetCurrentContext()** method:
```javascript JavaScript theme={null}
moe.resetCurrentContext();
```
# Self Handled In-App Messaging
The SDK does not show self-handled In-Apps. While creating the campaign, a JSON payload has to be provided in the dashboard. The same payload will be provided to the app by the SDK on calling the **getSelfHandledInApp()** method, and info will be received by a callback post all the delivery controls are checked:
```javascript JavaScript wrap theme={null}
var moe = MoECordova.init(YOUR_WORKSPACE_ID);
moe.getSelfHandledInApp();
//Callback for receiving self-handled inapp
moe.on('onInAppSelfHandle', function(selfHandledPayload) {
console.log('Self hanlded InApp Info: ' + JSON.stringify(selfHandledPayload));
});
```
Post consuming the Self-handled inApp, to update impression, click and dismiss stats call the following methods with the payload received in self-handled In-App callback:
```javascript JavaScript theme={null}
var moe = MoECordova.init(YOUR_WORKSPACE_ID);
// Track impression
moe.selfHandledShown(selfHandledPayload)
// Track Click
moe.selfHandledClicked(selfHandledPayload)
// Track Dismiss
moe.selfHandledDismissed(selfHandledPayload)
```
# In-App Messaging Rules
We use the following rules while showing the In-App:
Preconditions for In-App to work:
* If InApp Backend Sync was successful in the current session or not.
* Check if the Device is NOT iPad/Tablet.
* Check if InApp is disabled on the current screen.
* Check Device Orientation is Portrait(Landscape is not supported).
The following are checked for each campaign in the list of active campaigns(sorted according to priority and Last Updated Time)
* Check Global Delay has lapsed, skip this if **Ignore Global Delay** set for the campaign.
* Check if the campaign has expired
* Display Rules
* Check Show Only on Screen
* Check with current contexts
* Delivery Controls
* Persistence Check(If the primary action of InApp is done but still want to show the inApp)
* Check if the campaign has been shown max times
* Check if the campaign level delay has crossed.
The first campaign satisfying all the rules is shown to the user.
# Callback in JavaScript on In-App Events
To get a callback in javascript on in-app events you need to register for a click listener as shown below.
Minimum Plugin version required: 6.0.0
```javascript JavaScript wrap theme={null}
var moe = MoECordova.init(YOUR_WORKSPACE_ID);
moe.on('onInAppShown', function(inAppInfo) {
console.log('InApp Shown with Info: ' + JSON.stringify(inAppInfo));
});
moe.on('onInAppClick', function(inAppInfo) {
console.log('InApp Shown Clicked with Info: ' + JSON.stringify(inAppInfo));
});
moe.on('onInAppDismiss', function(inAppInfo) {
console.log('InApp Dismissed with Info: ' + JSON.stringify(inAppInfo));
});
moe.on('onInAppCustomAction', function(inAppInfo) {
console.log('InApp Custom Action with Info: ' + JSON.stringify(inAppInfo));
});
```
## Payload Structure in Callbacks
InAppInfo received in the callbacks above have the following structure:
```javascript JavaScript wrap theme={null}
{
"accountMeta": {
"appId": ""
},
"data": {
"platform": "iOS/android",
"campaignName": "",
"campaignId": "",
"campaignContext": {},
"actionType": "navigation",
"navigation": { // Key will only be present for onInAppClick callback
"navigationType": "screen/deep_linking",
"value": "",
"kvPair": {
"k1": "v1"
}
},
"customAction": {// Key will only be present for onInAppCustomAction callback
"kvPair": {
"k2": "v2"
}
},
"data": { // Key will only be present for onInAppSelfHandle callback
"payload": "", // payload entered in dashboard while creating selfHandled campaign
"dismissInterval": 60 // auto dismiss interval
}
}
}
```
# Handling Orientation Change
This is only for the Android platform.
Starting SDK version **7.3.0** in-apps are supported in both portrait and landscape modes. SDK has to be notified when the device orientation changes for SDK to handle in-app display.
There are two ways to do it:
1. Add the API call in the Android native part of your app
2. Call MoEngage plugin's **onOrientationChanged()**
## Add the API call in the Android native part of your app
Notify the SDK when **onConfiguartionChanged()** API callback is received in your App's Activity class.
```java Java wrap theme={null}
import android.os.Bundle;
import org.apache.cordova.\*;
import com.moengage.cordova.MoECordovaHelper;
public class MainActivity extends CordovaActivity {
...
@Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
MoECordovaHelper.getInstance().onConfigurationChanged();
...
}
...
}
```
## Call MoEngage plugin's orientation change API
Call the below API to notify SDK of the orientation change.
```javascript JavaScript wrap theme={null}
var moe = MoECordova.init(YOUR_WORKSPACE_ID);
...
// Notify SDK on orientation change
moe.onOrientationChanged();
```
# Migrating to 4.x.x
Source: https://moengage.com/docs/developer-guide/cordova-sdk/migration/migrating-to-4xx
Migrate your MoEngage Cordova plugin from version 3.2.0 or below to the 4.x.x release.
This migration is required only for people who have integrated plugin version 3.2.0 or below. If your plugin version is greater than 3.2.0 you can ignore this step.
# Removing existing plugin
Remove MoEngage SDK Plugin
```javascript JavaScript theme={null}
cordova plugin remove moengagesdk
```
Remove MoEngage Push Extension, if added. Use the command `cordova plugin list` to check whether the plugin is installed or not.
```javascript JavaScript theme={null}
cordova plugin remove moengagepushextension
```
# Install the new plugin
Install the new MoEngage SDK plugin.
```javascript JavaScript theme={null}
cordova plugin add moengagesdk --variable APP_ID="[YOUR_WORKSPACE_ID]"
```
### Variables
* **APP\_ID** = Workspace ID found under the settings page on the MoEngage dashboard.
# Initialize SDK(only for Android)
To initialize SDK create a class extending `Application` the class of Android and override `onCreate()`.\
Inside the `onCreate()` initialize the SDK as shown below.
```javascript JavaScript theme={null}
// this is the instance of the application class and "YOUR_WORKSPACE_ID" is the Workspace ID from the dashboard.
MoEngage moEngage =
new MoEngage.Builder(this, "YOUR_WORKSPACE_ID")
.build();
MoEngage.initialise(moEngage);
```
Make sure your application class is defined in the `AndroidManifest.xml` file as well.
# Setting up Push Notification(only Android)
MoEngage SDK supports both FCM and GCM, based on what you are using in your application install the appropriate plugin.\
The SDK no longer ships with Google's GCM or FCM library please ensure you have added the appropriate dependency in your app.
## FCM
```javascript JavaScript theme={null}
cordova plugin add cordova-moengage-fcm-dependency
```
## GCM
```javascript JavaScript theme={null}
cordova plugin add cordova-moengage-gcm-dependency
```
If MoEngage SDK is registering for push notification add the appropriate FCM/GCM listeners.
## FCM
```javascript JavaScript theme={null}
cordova plugin add cordova-moengage-fcm-listeners
```
## GCM
```javascript JavaScript theme={null}
cordova plugin add cordova-moengage-gcm-listeners
```
# Adding Push Related Meta Data
Refer to [Push Notification](/docs/developer-guide/android-sdk/push/basic/push-configuration) documentation and add all the required meta-data for push notifications to work.
# Android Notification Runtime Permissions
Source: https://moengage.com/docs/developer-guide/cordova-sdk/push/basic/android-notification-runtime-permissions
Handle Android 13 notification runtime permissions in your Cordova app using the MoEngage SDK.
Android 13 (API level 33) and higher supports [runtime permission](https://developer.android.com/guide/topics/permissions/overview#runtime) for sending [non-exempt](https://developer.android.com/develop/ui/views/notifications/notification-permission#exemptions) (including Foreground Services (FGS)) notifications from an app [POST\_NOTIFICATIONS](https://developer.android.com/reference/android/Manifest.permission#POST_NOTIFICATIONS). This change helps users focus on the notifications that are most important to them.
Refer to the [official documentation](https://developer.android.com/develop/ui/views/notifications/notification-permission) for more details.
The below-mentioned APIs are supported starting MoEngage core Android SDK version **12.3.01**
When an application runs on Android 13 and wants to show notifications to the user, it must request the user's notification permission. You have two options: let MoEngage handle permissions for you or handle the notification permission with your code.
* MoEngage handles Notification permission.
* You just have to call a single line of code mentioned on this page.
* You maintain the notification permission logic.
* Notify MoEngage SDK if permission to push notifications is granted.
We recommend you let MoEngage handle push notification permissions.
## Notify SDK of permission result
Once the application requests the user for notification permission notify the SDK of the user response using the below API. *If you are letting MoEngage SDK handle the notification permissions, you should ignore this step.*
```javascript JavaScript theme={null}
var moe = MoECordova.init("YOUR_WORKSPACE_ID");
//isGranted = true/false
moe.pushPermissionResponseAndroid(requestCount)
```
## Update the Permission request count(optional)
Once the application requests the user for notification permission, update the SDK of the request attempts.
**Why does the SDK require permission attempt count?**
SDK requires the attempt count to accurately track the number of times the permission request was attempted. If the user denies the permission request twice the application/SDK cannot request permission further i.e. if we request permission it would be automatically denied without the user seeing the request dialog. As an SDK we cannot be sure of the number of times the user has denied the permission we try to optimize the calls to request permission based on the attempt count.
```javascript JavaScript theme={null}
var moe = MoECordova.init("YOUR_WORKSPACE_ID");
moe.updatePushPermissionRequestCountAndroid(requestCount)
```
## Setup Notification Channels
If the application has already taken notification permission from the user call the below API to set up Notification Channels for showing push notifications.
```javascript JavaScript theme={null}
var moe = MoECordova.init("YOUR_WORKSPACE_ID");
moe.setupNotificationChannelsAndroid()
```
## Let MoEngage SDK handle Notification permission
MoEngage SDK provides helper APIs to show the permission request to the end-user or navigate the user to the settings screen for enabling notifications.
Use the below API to show the permission request dialog to the user.
When using the below API SDK automatically tracks the response, sets up the required notification channel, etc, mentioned above.
```javascript JavaScript theme={null}
var moe = MoECordova.init("YOUR_WORKSPACE_ID");
moe.requestPushPermissionAndroid();
```
## Navigate to Notification Settings (situational/optional)
Use the below API to show navigate the user to the Notification Settings for the application on the device.
*Note*: Below Android 8(API level 26) the user is directed to the Application Info screen of your application.
```javascript JavaScript theme={null}
var moe = MoECordova.init("YOUR_WORKSPACE_ID");
moe.navigateToSettingsAndroid();
```
# Android Push Configuration
Source: https://moengage.com/docs/developer-guide/cordova-sdk/push/basic/android-push-configuration
Configure Android push notifications in your Cordova app including FCM setup and push token handling.
## Basic Configuration
* **FCM Setup on MoEngage Dashboard** - FCM Authentication is the method to enable sending push notifications to your app installed on Android devices. Use the methods mentioned in [this article](/docs/developer-guide/android-sdk/push/basic/fcm-authentication) to authenticate MoEngage to access Firebase services.
## Passing Push Token
```javascript JavaScript theme={null}
var moe = MoECordova.init(YOUR_WORKSPACE_ID);
moe.passFcmToken()
```
## Passing Push Payload
```javascript JavaScript theme={null}
var moe = MoECordova.init(YOUR_WORKSPACE_ID);
moe.passFcmPayload()
```
Passing payload and token from JavaScript is only supported for Firebase Messaging Service.
We highly recommend you to use the Android native APIs for passing the push payload to the MoEngage SDK instead of the Cordova/JavaScript APIs. Cordova Engine might not get initialized if the application is in the killed state which will lead to poor push reachability or delivery.
## Customizing Push notification
If required the application can customize the behavior of notifications by using Native Android code (Java/Kotlin). To learn more about the customization refer to the [Advanced Push Configuration](/docs/developer-guide/android-sdk/push/advanced/callbacks-and-customisation) documentation.Instead of extending ***PushMessageListener*** as mentioned in the above document extend ***PluginPushCallback.***
Refer to the below documentation for Push Amp+, Push Templates, and Geofence.
* [Push Amplification](/docs/developer-guide/android-sdk/push/optional/push-amplification)
* [Push Amp Plus](/docs/developer-guide/android-sdk/push/optional/push-amp-plus/push-amp-plus-integration)
* [Push Templates](/docs/developer-guide/android-sdk/push/optional/push-templates)
* [GeoFence Push](/docs/developer-guide/android-sdk/push/optional/location-triggered)
# iOS Push Configuration
Source: https://moengage.com/docs/developer-guide/cordova-sdk/push/basic/ios-push-configuration-7xx
Configure iOS push notifications in your Cordova app including APNS certificates and push registration.
# Configuring Push Notifications in iOS
## APNS Certificate
To send push notifications in iOS, create an APNS certificate and upload it to the dashboard. Complete the following steps:
* [Create an APNS certificate](/docs/developer-guide/ios-sdk/push/basic/apns-certificate-pem-file-legacy)
* [Convert the resultant certificate to .pem format](/docs/developer-guide/ios-sdk/push/basic/apns-certificate-pem-file-legacy)
* [Upload .pem file to MoEngage Dashboard](/docs/developer-guide/ios-sdk/push/basic/apns-certificate-pem-file-legacy)
## Adding Push Entitlement to your Project
Once the APNS Certificate is uploaded, enable Push Entitlement in the Xcode project. For that select your app target, then go to Capabilities. Here enable the Push Notifications capability for your app as shown below :
## Uninstall Tracking
We make use of silent pushes to track uninstalls. For tracking uninstalls of all the users, enable Remote Notification background mode in-app capabilities for the same as shown below :
## Push Registration
After this you will have to register for push notification by using **registerForPushNotification** method of the plugin as shown below :
```javascript JavaScript theme={null}
var moe = new MoECordova.init();
moe.registerForPushNotification();
```
Push token generated is received from `onPushTokenGenerated` callback
```javascript JavaScript theme={null}
moe.on('onPushTokenGenerated', function (payloadInfo) {
console.log('pushToken generated' + JSON.stringify(payloadInfo));
});
```
## Rich Push and Templates Support:
Please refer to the Native iOS SDK docs for supporting Rich Push(images/videos/audio in the notification) and Templates in the app:
* [Rich Push](/docs/developer-guide/ios-sdk/push/basic/ios-push-integration-tutorial#optional)
* [Push Templates](/docs/developer-guide/ios-sdk/push/optional/push-templates)
# Push Callback
Source: https://moengage.com/docs/developer-guide/cordova-sdk/push/basic/push-callback
Set up JavaScript callbacks for push notification click events in the MoEngage Cordova SDK.
## Callback in JavaScript on Notification Click
To get a callback in javascript on notification click you need to register for a click listener as shown below.
Minimum Plugin version required : 3.0.0
```javascript JavaScript theme={null}
var moe = MoECordova.init(YOUR_WORKSPACE_ID);
moe.on('onPushClick', function(payloadInfo) {
//add logic here
});
```
## Payload
NotificationPayload received in the callback **onPushClick** will have the following structure:
```auto JSON theme={null}
{
"accountMeta": {
"appId": ""
},
"data": {
"platform": "android/iOS",
"isDefaultAction": false,
"clickedAction": {
"type": "navigation/customAction",
"payload": {
"type": "screenName/deepLink/richLanding",
"value": "",
"kvPair": {
"key1": "value1",
"key2": "value2"
...
}
}
},
"payload": {}
}
}
```
**accountMeta.appId** - The Workspace ID of MoEngage.\
**data.platform** - Native platform from which callback is triggered. Possible values - **android**, **ios**\
**data.isDefaultAction** - This key is present only for the Android Platform. It's a boolean value indicating if the user clicked on the default content or not. true if the user clicks on the default content else false.\
**data.clickedAction** - Action to be performed on notification click.\
**data.clickedAction.type** - Type of click action. Possible values **navigation** and **customAction**. Currently, **customAction** is supported only on Android.\
**data.clickAction.payload** - Action payload for the clicked action.\
**data.clickedAction.payload.type** - Type of navigation action defined. Possible values **screenName**, **deepLink**, **richLanding**. Currently, in the case of iOS, richlanding and deep-link URL are processed internally by the SDK and not passed in this callback therefore possible value in case of iOS is only **screenName**.\
**data.clickAction.payload.value** - value entered for navigation action or custom payload.\
**data.clickAction.payload.kvPair** - Custom key-value pair entered on the MoEngage Platform.\
**data.payload** - Complete campaign payload.
### Android Payload
If the user clicks on the default content of the notification the key-value pair and campaign payload can be found inside the **payload** key. If the user clicks on the action button or a push template action the action payload would be found inside **clickedAction**.\
You can use the **isDefaultAction** key to check whether the user clicked on the default content or not and then parse the payload accordingly.
### iOS Payload
In the case of iOS, you would always receive the key-value pairs with respect to clicked action in **clickedAction** key. Refer to this [link](/docs/developer-guide/ios-sdk/push/advanced/custom-notification-handling#notification-actions) for knowing the iOS notification payload structure.
# Location Triggered
Source: https://moengage.com/docs/developer-guide/cordova-sdk/push/optional/location-triggered
Add geofence-based location-triggered push notifications to your Cordova app using MoEngage.
# Installation
## Adding Geofence Plugin
Add **cordova-moengage-geofence** plugin to Cordova project as shown below :
```auto Shell theme={null}
$ cordova plugin add cordova-moengage-geofence
```
## Android Installation
## Install using BOM
Integration using BOM is the recommended way of integration; refer to the [Install Using BOM ](/docs/developer-guide/android-sdk/sdk-integration/basic-integration/Install-Using-BOM)document. Once you have configured the BOM add the dependency in the *app/build.gradle* file as shown below:
```json build.gradle wrap theme={null}
dependencies {
...
implementation("com.moengage:geofence")
}
```
Once the BOM is configured, include the specific MoEngage modules required for the application. \
Note: Version numbers are not required for these dependencies; the BOM automatically manages them.
## Manual Installation

Once you install the Flutter Plugin add MoEngage's native Android SDK dependency to the Android project of your application.\
Navigate to **android/app/build.gradle**. Add the MoEngage Android SDK's dependency in the **dependencies** block.
```json build.gradle theme={null}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation("com.moengage:geofence:$sdkVersion")
}
```
where **\$sdkVersion**should be replaced by the latest version of the MoEngage Geofence SDK
## iOS
In the case of iOS, the native dependency is part of the Geofence Cordova SDK itself, so there is no need to include any additional dependency for supporting Geofence.
## Configuration
### Start Geofence Monitoring
After integrating the geofence package call **startGeofenceMonitoring()** method to initiate the geofence module, this will fetch the geofences around the current location of the user. Please take a look at the [iOS doc](/docs/developer-guide/ios-sdk/push/optional/location-triggered) and [Android doc](/docs/developer-guide/android-sdk/push/optional/location-triggered) for more information on Geofence. By default, the geofence feature is not enabled. You need to call the \*\*startGeofenceMonitoring()\*\*to receive location-triggered push messages.
```auto Javascript theme={null}
MoEGeofence.startGeofenceMonitoring("YOUR_WORKSPACE_ID");
```
### Stop Geofence Monitoring
If you want to stop the geofence monitoring or feature use the **stopGeofenceMonitoring()** API. This API will remove the existing geofences.
```javascript Javascript wrap theme={null}
MoEGeofence.stopGeofenceMonitoring("YOUR_WORKSPACE_ID");
```
# Limitations
Source: https://moengage.com/docs/developer-guide/cordova-sdk/sdk-integration/limitations
Review unsupported features and native integration requirements when using the MoEngage Cordova plugin.
Compared to the Native Android/iOS SDKs there are a certain set of features we either do not support or it requires native Android/iOS implementation when using our Cordova plugin.
# Features not supported
* Nudges
* Action Buttons in iOS Notifications
# Features that are supported but requires Native Integration
* Inbox Module
* Cards Module
# Android SDK Initialization
Source: https://moengage.com/docs/developer-guide/cordova-sdk/sdk-integration/sdk-initialization/android-sdk-initialization
Initialize the MoEngage SDK in your Android application's onCreate method for Cordova integration.
# Initialization
Get the Workspace ID from the **Settings Page Dashboard --> Settings --> App --> General** on the MoEngage dashboard and initialize the MoEngage SDK in the **Application** class's **onCreate()**
It is recommended that you initialize the SDK on the main thread inside **onCreate()** and not create a worker thread and initialize the SDK on that thread.
```Java Java theme={null}
import com.moengage.cordova.MoEInitializer;
import com.moengage.core.MoEngage;
import com.moengage.core.DataCenter;
// this is the instance of the application class and "YOUR_WORKSPACE_ID" is the Workspace ID from the dashboard.
MoEngage.Builder moEngage = new MoEngage.Builder(this,"YOUR_WORKSPACE_ID”, DataCenter.DATA_CENTER_X);
MoEInitializer.initialiseDefaultInstance(this, moEngage);
```
Following details of the different data centers you need to set based on the dashboard hosts
| Data Center | Dashboard host |
| -------------------------- | ------------------------- |
| DataCenter.DATA\_CENTER\_1 | dashboard-01.moengage.com |
| DataCenter.DATA\_CENTER\_2 | dashboard-02.moengage.com |
| DataCenter.DATA\_CENTER\_3 | dashboard-03.moengage.com |
| DataCenter.DATA\_CENTER\_4 | dashboard-04.moengage.com |
| DataCenter.DATA\_CENTER\_5 | dashboard-05.moengage.com |
In case your application does not have an Application class yet navigate to java source code inside the android platform folder and add the Application class file.
Make sure your application class is defined in the **AndroidManifest.xml** file as well.\
Refer to the [API reference doc](https://moengage.github.io/android-api-reference/core/com.moengage.core/-mo-engage/-builder/index.html) for a detailed list of possible configurations.
# Exclude MoEngage Storage File from Auto-Backup
This is a mandatory integration step and is very essential to prevent your data from getting corrupted. Android's auto back-up service periodically backs up Shared Preference files, Database files, etc, more details [here](https://developer.android.com/guide/topics/data/autobackup). This backup results in MoEngage SDK's identifiers being backed up and restored after re-install.This restoration of the identifier results in your data being corrupted and users not being reachable via push notifications.
To ensure data is not corrupted after a backup is restored, opt out of MoEngage SDK's storage files. Refer to [Exclude MoEngage Storage File from the Auto-Backup](/docs/developer-guide/android-sdk/sdk-integration/basic-integration/exclude-mo-engage-storage-file-from-auto-backup) section of the documentation to learn more about which files to exclude.
# Framework Initialization
Source: https://moengage.com/docs/developer-guide/cordova-sdk/sdk-integration/sdk-initialization/framework-initialization
Initialize the MoEngage Cordova plugin in your application using the MoECordova.init() method.
# Initialise Cordova Component
Initialise MoEngage plugin in the ***index.js*** of your application by calling the \*\*\*MoECordova.init()\*\*\*in the ***onDeviceReady()***.
```javascript JavaScript theme={null}
var moe = MoECordova.init("YOUR_WORKSPACE_ID");
```
Refer to the following for platform-specific initialization:
* [Android](/docs/developer-guide/cordova-sdk/sdk-integration/sdk-initialization/android-sdk-initialization)
* [iOS](/docs/developer-guide/cordova-sdk/sdk-integration/sdk-initialization/ios-sdk-initialization)
# iOS SDK Initialization
Source: https://moengage.com/docs/developer-guide/cordova-sdk/sdk-integration/sdk-initialization/ios-sdk-initialization
Initialize the MoEngage Cordova SDK in your iOS application using code-based initialization.
## Code Initialization
From plugin version 8.0.0 we support initialization only via code. In the version 8.0.0 we have removed the initialization of SDK support via info.plist which was supported in 7.1.1 and above.
* Call any one of the below-given initialization methods in **application:didFinishLaunchingWithOptions:** method. The method accepts the **MoEngageSDKConfig** instance as its parameter. Refer [doc](/docs/developer-guide/ios-sdk/sdk-integration/basic/sdk-initialization) for more info on all the properties which can be configured using **MoEngageSDKConfig**.
```objective-c Objective-C wrap theme={null}
/// @param sdkConfig MoEngageSDKConfig instance for SDK configuration
/// @param launchOptions Launch Options dictionary
- (void)initializeDefaultSDKConfig:(MoEngageSDKConfig*)sdkConfig andLaunchOptions:(NSDictionary*)launchOptions;
/// @param sdkConfig MoEngageSDKConfig instance for SDK configuration
/// @param sdkState Enum indicating if SDK is Enabled/Disabled
/// @param launchOptions Launch Options dictionary
- (void)initializeDefaultSDKConfig:(MoEngageSDKConfig*)sdkConfig withMoEngageSDKState:(MoEngageSDKState)sdkState andLaunchOptions:(NSDictionary*)launchOptions;
```
* Sample code to initialise from **application:didFinishLaunchingWithOptions:** method:
```objectivec Objective-C wrap theme={null}
#import "AppDelegate.h"
#import "MainViewController.h"
// Make sure to import "AppDelegate+MoEngage.h"
#import "AppDelegate+MoEngage.h"
@implementation AppDelegate
- (BOOL)application:(UIApplication*)application didFinishLaunchingWithOptions:(NSDictionary*)launchOptions
{
self.viewController = [[MainViewController alloc] init];
MoEngageSDKConfig *sdkConfig = [[MoEngageSDKConfig alloc] initWithAppID: @"YOUR_WORKSPACE_ID"];
sdkConfig.moeDataCenter = DATA_CENTER_01; // Possible Values DATA_CENTER_01/DATA_CENTER_02/ DATA_CENTER_03
sdkConfig.appGroupID = "App Group ID";
// ---
// Update other Parameters of SDK Config
[self initializeDefaultSDKConfig:sdkConfig andLaunchOptions:launchOptions];
return [super application:application didFinishLaunchingWithOptions:launchOptions];
}
@end
```
# Data Center
In case your app wants to redirect data to a specific zone due to any data regulation policy please configure the zone in the MOSDKConfig object.
For more information on Data Center, refer [here](/docs/developer-guide/ios-sdk/sdk-integration/basic/data-center).
# Android SDK Installation
Source: https://moengage.com/docs/developer-guide/cordova-sdk/sdk-integration/sdk-installation/android-sdk-installation
Add the MoEngage Android SDK dependency to your Cordova project using BOM or manual configuration.
# Adding Dependency
To add MoEngage's Android SDK to your application, \/platforms/android/app` and create...` a file with the name **build-extras.gradle**. If you already have this file in your project, you need not create another one.
## Option 1: Using BOM (Recommended)

Use the Bill of Materials (BOM) to automatically manage compatible versions of the SDK modules.
```auto build.gradle theme={null}
dependencies {
...
// Import the MoEngage BOM
implementation(platform("com.moengage:android-bom:BOM_VERSION"))
// Add optional modules as needed
implementation("com.moengage:inapp")
}
```
Replace \*\*\`** with the latest BOM version.`. For more info on integration using BOM, refer [here](/docs/developer-guide/android-sdk/sdk-integration/basic-integration/Install-Using-BOM).
## Option 2: Manual Integration
Once the file is created, add the MoEngage SDK dependency to it.
```auto Groovy theme={null}
// adding the repositories block is optional. You can choose not to add this block if you have already added these repositories to your project.
repositories {
google()
mavenCentral()
}
dependencies {
implementation("androidx.core:core:1.6.0")
implementation("androidx.appcompat:appcompat:1.3.1")
implementation("androidx.lifecycle:lifecycle-process:2.7.0")
implementation("com.moengage:moe-android-sdk:13.02.00")
}
```
Replace **\$sdkVersion** with the appropriate SDK version
You can add the firebase messaging dependency also to this file itself if your file does not already have firebase messaging added.
# Framework Dependency
Source: https://moengage.com/docs/developer-guide/cordova-sdk/sdk-integration/sdk-installation/framework-dependency
Add the cordova-moengage-core plugin to your Cordova project and configure platform settings.
Connect your IDE to the [MoEngage docs MCP server](/docs/developer-guide/connect-your-ide-to-moengage-docs) for accurate, context-aware SDK guidance inside your development environment. Your assistant searches this documentation directly, helping you move through the integration with confidence.
Review the [SDK Deprecation Policy](/docs/developer-guide/sdk-lifecycle-and-policies/sdk-deprecation-policy) for supported SDK versions and deprecation timelines before integrating.
# Adding MoEngage Plugin
Add the **cordova-moengage-core** plugin to the Cordova project as shown below :
```Shell Shell theme={null}
$ cordova plugin add cordova-moengage-core
```
After installing the plugin, use the following platform-specific configuration.
* [Android SDK Installation](/docs/developer-guide/cordova-sdk/sdk-integration/sdk-installation/android-sdk-installation)
* [iOS SDK Installation](/docs/developer-guide/cordova-sdk/sdk-integration/sdk-installation/ios-installation)
# iOS Installation
Source: https://moengage.com/docs/developer-guide/cordova-sdk/sdk-integration/sdk-installation/ios-installation
Install the MoEngage iOS SDK in your Cordova project using CocoaPods dependency management.
# SDK Installation
## Installation using CocoaPods (Plugin Version 5.0.0 and above)
Starting from **cordova-ios** version 4.3.0 and **cordova-cli** version 6.4.0, CocoaPod support is provided to bundle any iOS framework. Therefore, we have updated our plugin too to support this from version 5.0.0.
Cocoapods is a dependency manager for iOS projects and makes integration easier. If you don't have cocoapods installed, you can do it by executing the following command in your terminal:
```Ruby Ruby theme={null}
sudo gem install cocoapods
```
Here, after adding the plugin just go to the **ios** folder in **platforms** and run **pod install** command to integrate our **MoEngagePluginBase**:
```Ruby Ruby theme={null}
pod repo update
pod install
```
### Add script to remove Unwanted Architectures
**Do this if not already done:** Select App Target and go to Build Phase and add a Run Script step to your build steps, set it to use **/bin/sh** and enter the following script:
```Shell Shell theme={null}
APP_PATH="${TARGET_BUILD_DIR}/${WRAPPER_NAME}"
# This script loops through the frameworks embedded in the application and
# removes unused architectures.
find "$APP_PATH" -name '*.framework' -type d | while read -r FRAMEWORK
do
FRAMEWORK_EXECUTABLE_NAME=$(defaults read "$FRAMEWORK/Info.plist" CFBundleExecutable)
FRAMEWORK_EXECUTABLE_PATH="$FRAMEWORK/$FRAMEWORK_EXECUTABLE_NAME"
echo "Executable is $FRAMEWORK_EXECUTABLE_PATH"
EXTRACTED_ARCHS=()
for ARCH in $ARCHS
do
echo "Extracting $ARCH from $FRAMEWORK_EXECUTABLE_NAME"
lipo -extract "$ARCH" "$FRAMEWORK_EXECUTABLE_PATH" -o "$FRAMEWORK_EXECUTABLE_PATH-$ARCH"
EXTRACTED_ARCHS+=("$FRAMEWORK_EXECUTABLE_PATH-$ARCH")
done
echo "Merging extracted architectures: ${ARCHS}"
lipo -o "$FRAMEWORK_EXECUTABLE_PATH-merged" -create "${EXTRACTED_ARCHS[@]}"
rm "${EXTRACTED_ARCHS[@]}"
echo "Replacing original executable with thinned version"
rm "$FRAMEWORK_EXECUTABLE_PATH"
mv "$FRAMEWORK_EXECUTABLE_PATH-merged" "$FRAMEWORK_EXECUTABLE_PATH"
done
```
This script is for removing unsupported architectures while exporting the build OR submitting app to the app store.
**For Plugin version 3.2.0 and below:**
Open your project in Xcode, select your project. Go to **Build Settings -> Linker -> Other Linker Flags** and add **-ObjC** flag
# Troubleshooting and FAQs - Cordova
Source: https://moengage.com/docs/developer-guide/cordova-sdk/troubleshooting-and-faqs/troubleshooting-and-faqs-cordova
Find answers to common MoEngage Cordova SDK questions and Android-specific troubleshooting steps.
## What is MoEDebuggerActivity?
The MoEngage SDK bundles the native MoEngage Android SDK, so your Android build includes `MoEDebuggerActivity`, a component that supports on-device SDK debugging. Refer to [What is MoEDebuggerActivity?](/docs/developer-guide/android-sdk/troubleshooting-and-faqs/troubleshooting-and-faqs) to understand what it does.
To remove it from your app, add the following to your Android project's `AndroidManifest.xml`:
```xml theme={null}
```
# Magento
Source: https://moengage.com/docs/developer-guide/ecommerce-platforms/magento/magento
Integrate MoEngage Web SDK into your Magento store using Google Tag Manager or direct integration.
# Overview
Magento is an open source ecommerce platform, that more than 150K online stores use. This document covers the multiple ways and the steps involved in integrating MoEngage Web SDK into your Magento store.
MoEngage / Magento integration can be done in two ways:
* Via Google Tag Manager
* Direct Integration
## Using Google Tag Manager
* Add Google Tag Manager (GTM) to your Magento store. Refer to this [Magento documentation](https://docs.magento.com/user-guide/v2.3/marketing/google-enhanced-ecommerce.html) for detailed steps.
* Complete the GTM / MoEngage integration by following the integration steps [here](https://partners.moengage.com/hc/en-us/articles/18795331766676).
* You can track any user event and/or User properties from your store to MoEngage via GTM.
This integration allows you to further integrate with the following MoEngage channels:
| Channel | Supported? |
| ------------------- | ---------- |
| On-site messaging | Yes |
| Web personalization | Yes |
| WebPush | No |
## Direct Integration with Magento
Please note that Magento versions below 2.2.0 are not supported.
* Install and set up your Magento store. Refer to the Magento [documentation](https://experienceleague.adobe.com/en/docs/commerce-operations/installation-guide/composer) for help.
* Login into your store's admin panel, and Navigate to Admin Panel > Content > Configuration
* Please choose the store view you want the head tag to be changed on or select *Global* to change it on every store view.\\
* Find the **HTML Head** section and add the [SDK script](/docs/developer-guide/web-sdk/web-sdk-integration/basic-integration/web-sdk-integration) code in the **Scripts and Style Sheets** field.\\
* Please save the configuration and flush the cache.
To overcome any potential customer data privacy issues, please whitelist the MoEngage WebSDK URLs. Follow their official [documentation](https://developer.adobe.com/commerce/php/development/security/content-security-policies#configure-a-modules-csp-mode) to add the rules. [Here](/docs/developer-guide/web-sdk/web-sdk-integration/basic-integration/web-sdk-integration) is the list of URL that you should whitelist.
### Data Tracking
In Magento, to track events or user attributes on the existing elements, you would need to edit the default HTML. For example, to track an event on click of "Add to Cart" button:
Add a script in your phtml file where the Add to Cart button is defined. You can usually find the add to cart button in catalog/product/view/addtocart.phtml file.
```php Code theme={null}
```
This integration allows you to use the MoEngage for:
| Channel name | Supported? |
| ------------------------------------- | ------------------------------------------------------------------------------------------------------------------------ |
| On-site messaging | Yes |
| Web personalization | Yes ([integration](/docs/developer-guide/personalize-sdk/sdk-integration/web-personalization-v2) script should be added) |
| User Event and User property tracking | Yes |
| WebPush | Yes |
For Webpush support service worker file can be added to the root of the project; usually inside /pub folder. This requires changes in the .htaccess file. If this is not possible for any reason, then please put the serviceworker inside the media folder or any custom folder inside the /pub directory.
If the service worker file is not placed in the root directory, then swPath and swScope can be added while initializing the SDK.
# Events and User Data Tracking
Source: https://moengage.com/docs/developer-guide/ecommerce-platforms/shopify/events-and-user-data-tracking
Send behavioral data from your Shopify store to MoEngage to build segments, trigger campaigns, and personalize communication.
Events tracking and user data tracking allow you to send behavioral data from your Shopify store to MoEngage. After you track these events, you can use them to build segments, trigger campaigns, and personalize communication based on user actions on your store.
## Tracked Events
MoEngage tracks the following events when you enable them on your Shopify app during integration.
| Event Type | Event Name | Description |
| ---------------- | --------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Product Event | Product Viewed | MoEngage tracks this event when a user views a product page. Use this to send back-in-stock alerts or personalized product recommendations.
**Note**: This event is triggered only on the default Shopify product page template. Users who store that use custom product page templates require additional implementation. For more information, contact the MoEngage Support team |
| Product Event | Product Searched | MoEngage tracks this event when a user searches for a product. Use this to personalize recommendations based on search intent. |
| Cart Event | Shopify - Add To Cart / Shopify - Update Cart | MoEngage tracks this event when a user adds a product to the cart or updates the cart. Use this to trigger the cart-abandonment campaigns. |
| Cart Event | Removed from Cart | MoEngage tracks this event when a user removes a product from the cart. |
| User Login Event | Customer Registered | MoEngage tracks this event when a customer registers with the Shopify store. Use this for welcome messages and onboarding flows. |
| User Login Event | Customer Logged In | MoEngage tracks this event when a customer logs in to the store. |
| User Login Event | Customer Logged Out | MoEngage tracks this event when a customer logs out of the store. |
| Checkout Event | Shopify - Checkout Started | MoEngage tracks this event when the customer initiates checkout. |
| Checkout Event | Shopify - Checkout Updated | MoEngage tracks this event when the customer updates their cart during checkout. |
| Order Event | Shopify - Order Placed | MoEngage tracks this event when a customer completes a purchase. Use this for order confirmation messages, loyalty-program nudges, or subscription prompts. |
| Order Event | Shopify - Order Fulfilled | MoEngage tracks this event when you fulfill an order, and it is ready to ship. |
| Order Event | Shopify - Order Partially Fulfilled | MoEngage tracks this event when you fulfill some, but not all, items in an order. |
| Order Event | Shopify - Order Cancelled | MoEngage tracks this event when you cancel an order. |
| Order Event | Shopify - Refund Created | MoEngage tracks this event when you create a refund, either due to a cancellation or a complaint. Use this to send personalized communication and reduce churn. |
Do not use "moe\_" as a prefix when you name events, event attributes, or user attributes. It is a reserved system prefix. Using it might result in periodic blacklisting without prior communication.
## Types of Events
Two sources generate events in the MoEngage Shopify integration:
* **Webhook**: Shopify generates these events and sends them directly to MoEngage via the Shopify webhook system. These represent server-side signals that the Shopify backend triggers, for example, when a user places an order or starts a checkout.
* **Web SDK:** The MoEngage JavaScript SDK running in the browser generates these events based on user behavior on your store frontend — for example, a product page view or a search action.
Both sources capture some events, such as Add to Cart and Checkout Started. This means that two separate event records can appear in MoEngage for the same user action, each with a different name and different attributes.
### Recommended Event Version for Campaigns and Segmentation
For any event that both Webhook and the Web SDK track, use the Webhook version when building segments or triggering campaigns.
MoEngage receives webhook events directly from the Shopify backend; they are the more reliable signal for campaign logic. Web SDK events are useful for triggering on-site messages that must fire in real time within the browser session.
If you use both versions of the same event in a single campaign or journey, you will cause conflicts and prevent a single consistent event-triggered flow from running.
## Event Attribute Requirements
### Product URL and Image URL in Webhook Events
Webhook events do not include the product URL or image URL in their payloads. These attributes are present only when you capture the event by using the Web SDK.
**To obtain these attributes:** If you sync your product catalog to MoEngage, you can pull the product URL and image URL from the catalog at send time by using **Product set-based personalization**. For more information, refer to the [User Actions Model](/docs/user-guide/content/recommendations/basic-recommendations/user-actions-model).
### Shopify Customer Metafields
MoEngage does not sync Shopify customer metafields, custom data fields attached to the Shopify customer object, to MoEngage user profiles by default. MoEngage also does not include them in event attributes.
To pass this data in a custom manner, refer to the [**Track Custom User Attributes**](#track-custom-user-attributes) section below.
## Track Custom Events via Shopify Liquid
If you must track events beyond the MoEngage default set, for example, a wishlist action or a custom button click, you can add them using the MoEngage Web SDK in your Shopify theme's Liquid files.
**Before You Start**
This procedure requires editing your Shopify theme files, which requires developer access. Check with your web team before you make changes.
### Step 1: Identify the Correct Liquid File to Edit
Shopify themes use Liquid template files to render pages. To find the right file, perform the following steps:
1. Log in to your Shopify Admin.
2. Go to **Online Store** > **Themes**.
3. Locate the theme you want to edit and click the **Actions** (three dots) button.
4. Select **Edit code** from the drop-down menu.
5. Browse the `sections/` folder.
The file you edit depends on where the event should trigger:
* **Product page actions:** Edit `sections/product-template.liquid` or `sections/main-product.liquid`.
* **Collection page actions:** Edit the relevant collection section file.
* **All pages:** Edit `layout/theme.liquid`. Use this file sparingly.
### Step 2: Add the Track-Event Call
In the Liquid file, locate the HTML element the user interacts with. Add a JavaScript call to the MoEngage **track\_event** method.
```javascript No Attributes theme={null}
Moengage.track_event("EVENT_NAME");
```
```javascript With Attributes theme={null}
Moengage.track_event("EVENT_NAME", {
"attribute_1": "value_1",
"attribute_2": 2,
"attribute_3": 3.4
});
```
```liquid Example: Wishlist theme={null}
```
### Step 3: Validate That the Event Fires
To validate that the event fires correctly, refer to the [Validate Integration](/docs/developer-guide/ecommerce-platforms/shopify/validate-integration) documentation.
## Track Custom User Attributes
You can pass custom attributes (such as metafields) to MoEngage profiles by using the following workarounds.
### Liquid-Based Method for Public Metafields
If you enable storefront access for the metafield, it can be read directly in Shopify Liquid. To find the correct file and add the code, perform the following steps:
1. Log in to your Shopify Admin.
2. Go to **Online Store** > **Themes**.
3. Locate the theme you want to edit and click the **Actions** (three dots) button.
4. Select **Edit code** from the drop-down menu.
5. Open `layout/theme.liquid`.
6. Inside the `` tag, after you initialize the MoEngage SDK, add the following code:
```liquid theme={null}
{% if customer %}
{% endif %}
```
Replace *loyalty* and *tier* with the actual namespace and key of your metafield. The `{% if customer %}` guard ensures the code only runs when a customer is logged in.
### Server-Side Sync for App-Owned Metafields
If a third-party app created the metafield, storefront access is typically set to **None**. In this case, you must perform a server-side integration:
1. Subscribe to the `customers/update` Shopify webhook.
2. In your webhook handler, use the Shopify Admin API to read the customer's metafield values.
3. Call the MoEngage Data API to update the corresponding user attributes on the customer's profile.
## Sample Event Payloads
The payload samples below show the attributes available for reference when building segments and personalizing event data. Full payload samples for each event are shown below.
### Sample Payloads for Webhook Events
```json Shopify - Update Cart theme={null}
{
"Event Received Time": "17th July 2023, 03:25:55 pm",
"Product Title": "Sample Product",
"Updated At": "17th July 2023, 03:26:15 pm",
"Variation ID": "123456",
"Currency": "USD",
"cart_token": "c1-fcdd3207bee3eef9f98abefaf2ca...",
"Product ID": "845236547",
"Price": 555.99,
"Source": "Shopify",
"Vendor Name": "845236547",
"Created At": "17th July 2023, 03:25:55 pm",
"Quantity": 2
}
```
```json Removed From Cart theme={null}
{
"Event Received Time": "17th July 2023, 03:25:55 pm",
"Product Title": "Sample Product",
"Updated At": "17th July 2023, 03:26:15 pm",
"Variation ID": "123456",
"Currency": "USD",
"Product ID": "845236547",
"Price": 555.99,
"Source": "Shopify",
"Vendor Name": "845236547",
"Created At": "17th July 2023, 03:25:55 pm",
"Quantity": 2
}
```
```json Shopify - Checkout Started theme={null}
{
"Event Received Time": "17th July 2023, 03:25:55 pm",
"Product Title": "Sample Product",
"Updated At": "17th July 2023, 03:26:15 pm",
"ID": "3cce6aeedf786679ac03e043729fd...",
"Variation ID": "0:123456",
"Currency": "USD",
"Checkout ID": "3cce6aeedf786679ac03e043729fd...",
"Source Name": "web",
"cart_token": "c1-fcdd3207bee3eef9f98abefaf2ca...",
"Checkout URL": "https://store.myshopify.com/products/snowboard",
"Shopify Customer Id": 36838667059499,
"Total Price": 327,
"Price": 0:300.00,
"Subtotal Price": 300.00,
"Source": "Shopify",
"Vendor Name": "845236547",
"Created At": "17th July 2023, 03:25:55 pm",
"Quantity": 2,
"Product ID": "845236547"
}
```
```json Shopify - Checkout Updated theme={null}
{
"Event Received Time": "17th July 2023, 03:25:55 pm",
"Product Title": "0:Sample Product01",
"Updated At": "17th July 2023, 03:26:15 pm",
"ID": "3cce6aeedf786679ac03e043729fd...",
"Variation ID": "0:123456",
"Currency": "USD",
"Checkout ID": "3cce6aeedf786679ac03e043729fd...",
"Source Name": "web",
"cart_token": "c1-fcdd3207bee3eef9f98abefaf2ca...",
"Checkout URL": "https://store.myshopify.com/products/snowboard",
"Shopify Customer Id": 36838667059499,
"Total Price": 327,
"Price": [0:300.00],
"Subtotal Price": 300.00,
"Source": "Shopify",
"Vendor Name": "845236547",
"Created At": "17th July 2023, 03:25:55 pm",
"Quantity": [0:1],
"Product ID": [0:845236547]
}
```
```json Checkout Updated after Shipment theme={null}
{
"Event Received Time": "17th July 2023, 03:25:55 pm",
"Shipping Address Country": "US",
"Billing Address City": "New York",
"Product Title": "0:Sample Product01",
"cart_token": "c1-fcdd3207bee3eef9f98abefaf2ca...",
"ID": "3cce6aeedf786679ac03e043729fd...",
"Checkout ID": "3cce6aeedf786679ac03e043729fd...",
"First Name": "John",
"Currency": "USD",
"Shipping Address Country Code": "US",
"Billing Address Country Code": "US",
"Updated At": "",
"Source Name": "web",
"first_name": "John",
"Shipping Address Province": "New York",
"Checkout URL": "https://store.myshopify.com/products/snowboard",
"last_name": "Doe",
"Shopify Customer Id": 36838667059499,
"Total Price": 327,
"Price": 0:300.00,
"Shipping Address Zip": "10009",
"Subtotal Price": 300.00,
"Source": "Shopify",
"Shipping Address Province Code": "NY",
"Vendor Name": "845236547",
"Billing Address Country": "US",
"Created At": "17th July 2023, 03:25:55 pm",
"Variation ID": "0:123456",
"Quantity": [0:1],
"Product ID": [0:845236547]
}
```
```json Shopify - Order Placed theme={null}
{
"Event Received Time": "17th July 2023, 03:25:55 pm",
"Current Total Discounts": 0.00,
"Product Title": "0:Sample Product01",
"cart_token": "c1-fcdd3207bee3eef9f98abefaf2ca...",
"ID": "3cce6aeedf786679ac03e043729fd...",
"Name": "#1008",
"Order Status URL": "",
"Total Quantity": 1,
"Billing Address Country Code": "US",
"Billing Address Zip": 10009,
"Order ID": 12334534345,
"Currency": "USD",
"Total Price": 354,
"Checkout ID": "3cce6aeedf786679ac03e043729fd...",
"First Name": "John",
"Order Number": 1008,
"Financial_status": "paid",
"Source": "Shopify"
}
```
```json Order Partially Fulfilled theme={null}
{
"Event Received Time": "17th July 2023, 03:25:55 pm",
"product_title": [0:"product 2", 1:"product 2"],
"Updated At": "17th July 2023, 03:25:57 pm",
"product_id": [0:234234324, 1: 97897897897],
"variation_id": [0:23423423423, 1:23423423444],
"Total Spent": 5687,
"Currency": "USD",
"Total Price": 354,
"Checkout ID": "3cce6aeedf786679ac03e043729fd...",
"tracking_company": "1",
"vendor_name": [0:"ShopiWeb", 1:"ShopifyWeb"],
"OrderID": 34343455,
"item_fulfilment_status": [0:"partial", 1:"fulfilled"],
"Quantity": [0:4, 1:3],
"item_count": 2,
"order_fulfilment_status": "partial",
"Subtotal Price": 10500,
"Order Status URL": "https://yourshopifystore...",
"price": [0:1500.00, 1:1500.00],
"variation_title": [0:"product 2 - black", 1:"product 2 - red"],
"Financial status": "refunded",
"Created At": "17th July 2023, 03:25:55 pm"
}
```
```json Shopify - Order Fulfilled theme={null}
{
"Event Received Time": "17th July 2023, 03:25:55 pm",
"product_title": [0:"product 2"],
"cart_token": "c1-fcdd3207bee3eef9f98abefaf2ca...",
"product_id": [0:234234324],
"variation_id": [0:23423423423],
"Total Spent": 5687,
"Currency": "USD",
"Total Price": 354,
"Checkout ID": "3cce6aeedf786679ac03e043729fd...",
"Source Name": "web",
"vendor_name": [0:"ShopiWeb"],
"item_fulfilment_status": [0:"fulfilled"],
"Updated At": "17th July 2023, 03:25:58 pm",
"OrderID": 34343455,
"Quantity": [0:1],
"item_count": 1,
"order_fulfilment_status": "fulfilled",
"Subtotal Price": 300,
"Order Status URL": "https://yourshopifystore...",
"price": [0:1500.00, 1:1500.00],
"variation_title": [0:"product 2 - black"],
"Financial status": "paid",
"Created At": "17th July 2023, 03:25:55 pm"
}
```
```json Shopify - Order Cancelled theme={null}
{
"Event Received Time": "17th July 2023, 03:25:55 pm",
"product_title": [0:"product 2", 1:"product 2"],
"cart_token": "c1-fcdd3207bee3eef9f98abefaf2ca...",
"product_id": [0:234234324, 0:23423423446],
"variation_id": [0:23423423423, 0:84353458],
"Total Spent": 5687,
"Currency": "USD",
"Total Price": 354,
"Checkout ID": "3cce6aeedf786679ac03e043729fd...",
"cancelled at": "17th July 2023, 03:25:55 pm",
"Source Name": "web",
"vendor_name": [0:"ShopiWeb", 1:"ShopiWeb"],
"item_fulfilment_status": [0:"fulfilled"],
"Updated At": "17th July 2023, 03:25:58 pm",
"OrderID": 34343455,
"Quantity": [0:1, 1:2],
"item_count": 2,
"order_fulfilment_status": [0: , 1:],
"Subtotal Price": 300,
"Order Status URL": "https://yourshopifystore...",
"price": [0:1500.00, 1:1500.00],
"variation_title": [0:"product 2 - black", 1:"product 2 - red"],
"Financial status": "refunded",
"Created At": "17th July 2023, 03:25:55 pm"
}
```
```json Shopify - Refund Created theme={null}
{
"Event Received Time": "17th July 2023, 03:25:55 pm",
"Variation Title": [0:"Product 2 Black"],
"Product Title": [0:"Product 2"],
"Updated At": "17th July 2023, 03:25:59 pm",
"OrderID": 34343455,
"Variation ID": [0:234234234],
"Currency": "USD",
"email": "John.doe@example.com",
"user_id": 1545645465,
"Refund Created At": "17th July 2023, 03:25:55 pm",
"Source": "Shopify",
"Vendor Name": [0:"ShopifyWeb"],
"Created At": "17th July 2023, 03:25:55 pm",
"price": [0:500.00],
"Quantity": [0:1],
"Product ID": [0:8923423]
}
```
### Sample Payloads for Web SDK Events
```json Product Viewed theme={null}
{
"Event Received Time": "17th July 2023, 03:25:55 pm",
"Email": "john.doe@example.com",
"Product Title": "Sample Product",
"Variation ID": "123456",
"Total Variants": 3,
"Product Handle": "Product 1",
"Available": true,
"Currency": "USD",
"Product ID": "845236547",
"Price": 555.99,
"Source": "Shopify",
"Quantity": 3,
"First Session": true,
"URL": "https://yourstore.myshopify.com/products/view"
}
```
```json Product Searched theme={null}
{
"Event Received Time": "17th July 2023, 03:25:55 pm",
"Variation ID": "123456",
"Currency": "USD",
"URL": "Search URL",
"Search String": "Sneakers Green",
"Source": "Shopify",
"First Session": true
}
```
```json Add to Cart theme={null}
{
"Event Received Time": "17th July 2023, 03:25:55 pm",
"Email": "john.doe@example.com",
"Product Title": "Sample Product",
"Variation ID": "123456",
"Variation Title": "Sea Green",
"Total Variants": 3,
"Product Handle": "Product 1",
"Available": true,
"Currency": "USD",
"Product ID": "845236547",
"Vendor Name": "845236547",
"Price": 555.99,
"Source": "Shopify",
"Quantity": 3,
"First Session": true,
"Product URL": "/product/type?type=Sneakers"
}
```
```json Update Cart theme={null}
{
"Event Received Time": "17th July 2023, 03:25:55 pm",
"Email": "john.doe@example.com",
"Product Title": "Sample Product",
"Variation ID": "123456",
"Variation Title": "Sea Green",
"Total Variants": 3,
"Product Handle": "Product 1",
"Available": true,
"Currency": "USD",
"Product ID": "845236547",
"Vendor Name": "845236547",
"Price": 555.99,
"Source": "Shopify",
"Quantity": 3,
"First Session": true,
"Product URL": "/product/type?type=Sneakers"
}
```
```json Removed From Cart theme={null}
{
"Event Received Time": "17th July 2023, 03:25:55 pm",
"Email": "john.doe@example.com",
"Product Title": "Sample Product",
"Variation ID": "123456",
"Currency": "USD",
"Product ID": "845236547",
"Price": 555.99,
"Source": "Shopify",
"Quantity": 3
}
```
```json Customer Registered theme={null}
{
"Event Received Time": "17th July 2023, 03:25:55 pm",
"Email": "john.doe@example.com",
"Source": "Shopify",
"First Session": true,
"URL": "https://yourstore.myshopify.com/"
}
```
```json Customer Logged In theme={null}
{
"Event Received Time": "17th July 2023, 03:25:55 pm",
"Email": "john.doe@example.com",
"FirstName": "John",
"LastName": "Doe",
"Customer ID": 7385786908975,
"URL": "https://yourstore.myshopify.com/account"
}
```
```json Checkout Started theme={null}
{
"Event Received Time": "17th July 2023, 03:25:55 pm",
"Email": "john.doe@example.com",
"currency": "USD",
"Total Price": 699.95,
"Product Prices": [699.95],
"Product IDs": ["8869819679023"],
"Product Quantities": [1],
"Product Titles": ["The Complete Snowboard - Ice"],
"Variation IDs": ["46822634324271"],
"Vendor Names": ["Snowboard Vendor"],
"Source": "Shopify",
"First Session": true,
"URL": "https://yourstore.myshopify.com/products/the-complete-snowboard"
}
```
## Tracked User Properties
MoEngage tracks the following user attributes when you enable them during integration.
| Attribute Name | Description |
| -------------- | ------------------------------ |
| First Name | The first name of the user. |
| Last Name | The last name of the user. |
| Mobile | The mobile number of the user. |
| Email | The email address of the user. |
| Shopify ID | The user's ID within Shopify. |
| Shopify LTV | The lifetime value of the user |
## Configuration for Event and User Tracking
To select the events and user properties that MoEngage tracks, perform the following steps:
1. Navigate to the **Configuration** tab in your Shopify app.
2. Select the user events and properties you want to track.
3. Click **Save configuration**.
If you have questions about your Shopify integration, refer to the [MoEngage Shopify FAQs](/docs/developer-guide/ecommerce-platforms/shopify/faqs).
# FAQs
Source: https://moengage.com/docs/developer-guide/ecommerce-platforms/shopify/faqs
Find answers to common questions about the installation, configuration, and use of the MoEngage Shopify integration.
This article answers common questions about the installation, configuration, and use of the MoEngage Shopify integration—covering setup, identity, data sync, catalog, and event tracking.
## Installation
To troubleshoot your account authentication, review the following question:
Newly generated API keys take up to 10 minutes to activate. Wait 10 minutes after you generate the key, and then retry authentication for the Shopify app. If the error persists, verify that you copied the full key with no leading or trailing spaces.
When you uninstall the Shopify public app, MoEngage schedules the deletion of your data and configuration, which can take up to 48 hours to complete. If you reinstall the app within that window, the **Save Configuration** button can remain disabled and the configuration stays stuck, so the webhook subscription is not recreated and events do not flow.
To resolve this, either wait for the deletion to complete before reinstalling, or reset the configuration as follows:
1. Uncheck all events and attributes.
2. Click **Save Configuration**.
3. Re-check all the events and attributes.
4. Click **Save Configuration** again.
## User Identity
To manage user profiles and identifiers, review the following questions:
The unique user identifier setting determines whether the mobile number or email is used as the primary key to identify each user on the MoEngage dashboard. When the same user visits your store from multiple devices, MoEngage merges their activity into a single profile if the same unique identifier is present across all their devices and browsers.
By default, Shopify uses email to identify users. The right choice depends on your business: use the identifier your customers most likely provide at checkout, and whichever aligns with how you identify users across your other systems.
If you already use MoEngage, select the same unique identifier you currently use. If neither email nor mobile number matches your current setup, contact the support team or your account executive for help.
No, currently the setting is limited to mobile number or email. You must select one at the time of installation.
**Selection is permanent after installation**
The identifier you select at setup applies permanently to your workspace. If you change the identifier later, you must reinstall the integration, which affects all existing user profiles. Choose the identifier that will most reliably be present for your customers at checkout.
Not at the individual identifier setting level—you must select one at installation. However, when you enable Identity Resolution for your workspace, MoEngage recognizes the same user via multiple identifiers simultaneously. See the Identity Resolution section below for details.
Before you select the mobile number as your UID, you must make the following changes to your Shopify store:
* The Shopify checkout flow does not collect a mobile number by default. Enable this from your store checkout settings before installation.
* The default user signup and login flow also does not collect the mobile number. Implement custom login and signup flows to capture the user mobile number at these touchpoints.
If these changes are not made before you go live, you will see a high volume of anonymous profiles because MoEngage cannot assign a UID to users who have not provided a mobile number.
No. If you use a shipping mobile number as the UID, data corruption occurs. A single user can place multiple orders with different shipping mobile numbers—each creates a separate profile in MoEngage, which breaks the single customer view and causes attribution errors.
For example, a user Bob places an order with shipping number SM1—MoEngage creates a profile with UID SM1. Bob then places another order with shipping number SM2—MoEngage creates a second profile with UID SM2. On Shopify, you see one user with two orders. On MoEngage, you see two users with one order each. Abandoned cart campaigns fire incorrectly because SM1 fulfills the "Add to Cart" criteria but not "Order Placed".
To avoid this miscommunication and data discrepancy, do not use the shipping mobile number as the UID.
MoEngage merges profiles only when their UID values match. If the same user visits your store from multiple devices but does not provide the UID attribute at each touchpoint, MoEngage cannot merge those sessions and creates a separate profile for each.
For example, if email is set as UID and a user places two orders from different devices without entering their email during either checkout—by using only a mobile number—two separate profiles appear in MoEngage, both with the same mobile number but no matching UID to trigger a merge.
This happens when the mobile number is not present in the user Shopify profile at the time the track event occurs. When MoEngage cannot find the selected UID, it may fall back to the next available identifier. The most common cause is customers completing checkout without entering a phone number.
To resolve this consistently, contact your Customer Success Manager or raise a support ticket requesting Identity Resolution enablement. With Identity Resolution enabled, MoEngage tracks users across multiple identifiers simultaneously, which prevents this fallback behavior.
When a user adds items to their cart without identified data (no email or phone number available), MoEngage uses the Shopify cart token as a temporary identifier to preserve abandoned cart event data.
If you see a high volume of cart-token profiles, Identity Resolution is the recommended solution. Once enabled, MoEngage can merge these anonymous profiles with the identified profile when the user provides an email or phone number. Contact your Customer Success Manager or raise a support ticket to request enablement.
## [Identity Resolution](/docs/developer-guide/ecommerce-platforms/shopify/user-profile-management-with-shopify)
To unify user profiles from multiple sources, review the following questions:
Identity Resolution is a MoEngage feature that links user profiles from multiple sources into a single unified profile. Once it is enabled, MoEngage can recognize the same user using multiple identifiers—for example, both email and mobile number—rather than relying on a single value at every touchpoint. You can configure up to five identifiers per workspace.
Without Identity Resolution, each user is tracked by a single identifier. When that identifier is absent, MoEngage creates a separate anonymous profile. Identity Resolution prevents duplicate profiles, resolves identity gaps from guest checkouts, and ensures campaign triggers fire correctly across your full user base.
Identity Resolution requires enablement by the MoEngage team before you can configure it. You cannot activate it from within the Shopify app or the MoEngage dashboard.
To request enablement, contact your Customer Success Manager or raise a support ticket. Once it is enabled, go to **Settings** > **Data** > **Identity Resolution** in your MoEngage dashboard to configure your identifiers and merge rules.
No. Profile merges are irreversible—once two profiles merge, you cannot unmerge them.
**Review before you activate**
Before you enable Identity Resolution, review your identifier configuration and merge rules carefully. You can request a merge report from the support team before activation—this documents every user merge action.
## [User and Order Backfill](/docs/developer-guide/ecommerce-platforms/shopify/user-data-sync)
To sync historical store data, review the following questions:
The historical data sync brings two types of data into MoEngage:
* User profiles: All registered users available in your Shopify store at the time you run the synchronization, regardless of when those users were created.
* Order data: Orders placed during the date range you select, ingested as individual **Shopify—Order Placed** events. Each event carries the original order date from Shopify as its timestamp.
Two common causes occur:
* UID not present on user profiles: If you selected mobile number as the UID and some existing user profiles on Shopify do not have a mobile number, those users and their order data cannot sync automatically. Contact our support team to arrange a manual upload.
* The event retention period is shorter than the sync window: If your MoEngage event retention policy is shorter than the date range you try to sync, the sync is partial. You must increase the retention period for the **Shopify—Order Placed** event first, and then retry the sync.
**Set event retention before you sync beyond 60 days**
Contact your Customer Success Manager or support to increase your event retention period before you start the sync. This cannot apply retroactively to a sync that has already run.
A standard sync for the last 60 days typically completes within 24 hours. An extended 2-year sync can take up to 48 hours.
To confirm the sync completed correctly: wait at least a few hours after you start, and then open a user profile in MoEngage for a user you know who placed an order within your sync window. Check that the **Shopify—Order Placed** event appears with the correct original order date.
Check two things before you run the sync:
* Make sure your MoEngage account does not have existing duplicate data. If user and event data are already present, the sync creates duplicates and causes data corruption.
* Use a date range before your app install date. After installation, MoEngage automatically tracks new order data via webhooks and should already be available in your account.
If you request a synchronization window that is longer than your workspace's event retention period, MoEngage syncs only the orders that fall within the retention window. MoEngage does not store orders outside that window, even if the system successfully retrieves them from Shopify.
**Set event retention before syncing beyond 60 days**
Contact your Customer Success Manager or the MoEngage Support team to increase your event retention period before starting the synchronization. You cannot apply this change retroactively to a synchronization that has already run.
## [Catalog Sync](/docs/developer-guide/ecommerce-platforms/shopify/sync-product-catalog)
To synchronize your product catalog, review the following questions:
MoEngage fetches your full Shopify product catalog and syncs it into a new MoEngage catalog. This catalog is automatically created when you enable catalog sync in the app settings. The catalog is then available as a data source for product recommendation blocks, dynamic content in messages, and segmentation filters.
**Enable Basic Recommendations**
Catalog sync makes your product data available in MoEngage, but using it for personalization and product recommendations requires that you enable Basic Recommendations on your account.
After the first sync completes, MoEngage keeps the catalog up to date in real time. Price changes, flash sales, and inventory updates made in Shopify reflect in MoEngage without waiting for a scheduled refresh.
Real-time catalog sync is in early access.
MoEngage syncs only the products whose Shopify status is **Active**. If a product's status changes from **Active** to any other value, MoEngage removes it from the catalog.
You define the catalog schema yourself on the **Sync product catalog** tab. MoEngage prefills a default mapping that covers Variation ID, Product ID, Link, Image Link, Vendor Name, SKU, Variation Title, Currency, Price, Quantity, Description, Created At, and Updated At.
You can change the data type and Shopify source of any prefilled attribute, remove the ones you do not need, and map your own attributes, including your Shopify metafields. For more information, refer to [Sync Your Product Catalog](/docs/developer-guide/ecommerce-platforms/shopify/sync-product-catalog).
There are three common causes:
* A catalog field contains inconsistent data types across products. For example, if the variant name field holds string values for some products and numeric values for others, MoEngage rejects the products with non-conforming field types. The sync completes without an error, but the products are silently excluded.
* The products are not **Active** in Shopify. MoEngage syncs only Active products.
* The attribute you marked as unique holds the same value for more than one item. MoEngage treats those items as one and keeps only the most recent values it receives for that identifier, so the earlier item does not appear.
To diagnose this, compare the total product count in MoEngage against your active product count in Shopify. If significantly fewer products appear, review your Shopify product data for fields with mixed data types—particularly variant name, SKU, and price. Correct the inconsistencies and re-trigger the sync.
Add the attribute on the **Sync product catalog** tab before you request the first sync. Under **Add custom attributes**, enter a name, select a data type, and click **Add attribute**, then map it to a Shopify source. Your Shopify metafields are available as sources.
You can add new custom attributes at any time, and MoEngage includes them in the next sync.
The attributes already in the table, their data types, their Shopify sources, the unique identifier, and the catalog currency are fixed once the first sync completes successfully. Review your mapping before you request the first sync.
## [Event Tracking](/docs/developer-guide/ecommerce-platforms/shopify/events-and-user-data-tracking)
To monitor automated events, review the following questions:
Based on your app settings, MoEngage automatically tracks the events you enabled. Events are captured through a combination of the MoEngage Web SDK (for browser-side actions such as Product Viewed and Add to Cart) and Shopify Webhooks (for server-side signals such as Order Placed and Order Fulfilled).
**Which event version to use in campaigns**
For any event tracked by both the Web SDK and a Shopify Webhook—such as Add to Cart and Checkout Started—always use the Webhook version when you build segments or trigger campaigns. Webhook events are received directly from Shopify's backend and are the more reliable signal. Using both versions of the same event in a single campaign causes conflicts.
Discrepancies often have several causes:
* **Count is lower in MoEngage:** Shopify webhook delivery is not guaranteed. Webhook requests can occasionally be missed.
* **Count is higher in MoEngage:** Shopify can send duplicate webhook events for the same order. MoEngage applies deduplication, but some duplicates may still be tracked.
Differences with other analytics tools are expected because each platform uses a different counting methodology. Shopify is the source of truth for MoEngage data.
Shopify counts Add to Cart as the number of sessions that result in cart creation. MoEngage counts the number of times the Add to Cart event fired, which includes multiple adds within a single session. Use the MoEngage count for campaign triggers and the Shopify count for conversion funnel benchmarking.
Product URL and image URL are not included in Shopify webhook payloads, so they are absent from webhook-based events, including Shopify — Add to Cart, Shopify — Checkout Started, and Shopify — Order Placed.
To use product images or URLs in campaign personalization, use catalog enrichment at send time: set up your product catalog synchronization and reference catalog attributes in your message template. Alternatively, you can track these attributes directly by using custom SDK events.
Fulfillment events (Order Delivered, Out for Delivery) are available for all by default. Tracking these events requires you to allow these events to be tracked from your insalled Moengage Shopify App.
All Web SDK based events are tracked automatically on the default Shopify themes. If your store uses custom store theme, or you have customised the default theme, such customisation may hinder in automatic tracking of default webSDK events on those pages/stores. Additional custom event tracking is required to track those events or you can continue to use the webhook based alternatives.
Shopify customer metafields do not sync to user profiles and do not appear in event attributes by default. To pass metafield data to MoEngage, configure the metafields in Shopify:
* Public storefront access: If the metafield has public storefront access enabled, read it server-side in Shopify Liquid and pass it to MoEngage by using a user attribute call in your theme.
* No storefront access: If a third-party app creates the metafield, a server-side integration is required. Subscribe to the Shopify customers/update webhook, read the metafield value by using the Shopify Admin API, and then call the MoEngage Data API to update the user attribute.
**For implementation details**
See the "Shopify customer metafields" section in the [**Events and User Data Tracking**](/docs/developer-guide/ecommerce-platforms/shopify/events-and-user-data-tracking) article for step-by-step guidance on both paths, and along with a liquid code example.
For pre-checkout events, add the MoEngage [Chrome plugin](https://chromewebstore.google.com/detail/moengage-sdk/dhggnkfnnoebbfofpimfehcklnekmbgi) to your browser, and then perform the events again. If the plugin shows the events as tracked, then the track works—check that you look at the correct user profile.
For checkout and post-checkout events, check that the events are enabled for automated track in your app settings. Verify whether the event is tracked against a different profile by filtering on event attributes with your order ID, email, or mobile number.
First, use the MoEngage Chrome plugin to check whether the MoEngage SDK loads on your store. If the SDK is not detected:
* Go to your Shopify admin, open the MoEngage app embed settings, and then un-save and re-save the app embed block. This re-triggers SDK injection.
* If the SDK still does not load, check your browser console for JavaScript errors on the page.
If the SDK is loading correctly, events are still not flowing, perform the following steps:
1. Open your event tracking settings in the MoEngage Shopify app.
2. Uncheck a few events and save, and then re-check all events and save again.
3. Wait 5 minutes, and then perform the events in your store.
## Link Multiple Stores to a Single MoEngage Account
To understand the setup and limitations of linking multiple storefronts, review the following question:
Yes, although MoEngage does not recommend this approach. If you want to link multiple stores, use the same authentication keys during installation across your different stores to link them to the same MoEngage account.
Review the following implications before you proceed:
* **Web Push**
* The same user is prompted for separate web push permission on each store.
* Users who subscribe to web push on multiple stores receive duplicate notifications if campaigns are not scoped to a specific store.
* **OSM (On-site Messaging)**
* Specify a target URL for each campaign. Without it, all stores display the same campaign including to users who visit multiple stores.
* **Data and Profiles**
* Use the same user identifier across all stores to maintain a single customer view. Different identifiers per store result in multiple profiles for the same user.
* **Segmentation**
* Use either URL or store ID filters in the event attributes to identify the events for each store to isolate them when you build segments.
# Shopify 2.0
Source: https://moengage.com/docs/developer-guide/ecommerce-platforms/shopify/shopify-20
Install and configure the MoEngage app on your Shopify 2.0 store for cross-channel engagement.
# Overview
This article covers how to install and configure the MoEngage app on your Shopify store. Complete the steps below to authenticate, initialize, and configure the integration before you begin tracking events or syncing data.
## Step 1: Install the MoEngage App
To install the MoEngage app, perform the following steps:
1. In your MoEngage UI, navigate to the **App Marketplace**.
2. Search for **Shopify 2.0**, and then click the tile.
3. Navigate to the **Integrate** tab, and then click **Install MoEngage app on your Shopify** **store**. Use the MoEngage listing in the Shopify App Marketplace to add the app to your store.
4. Click **Open** to redirect to the Shopify admin UI and complete the installation.
## Step 2: Authenticate
After successful installation, to authenticate, perform the following steps:
1. Navigate to the **MoEngage App Integration** tab.
2. Enter the following credentials from your MoEngage account.
| Field | Description |
| ------------ | ------------------------------------------------------------------------------------------------------------------------------ |
| App ID | The Workspace ID of your MoEngage account is available at **Settings** > **Account** > **APIs** > **Workspace ID**. |
| Data API ID | Same as **Workspace ID**. |
| Data API Key | The Data API Key of the MoEngage account is available at **Settings** > **App Settings** > **APIs** > **API Keys** > **Data.** |
3. Click **Authenticate** and then click **Continue**.
**Information** Newly generated API keys can take up to 10 minutes to be activated. If authentication fails after entering new credentials, wait 10 minutes and then try again before raising a support ticket.
## Step 3: Configure Initialization Settings
**Note**
The following steps must be performed for MoEngage to track user data and events.
To configure the initialization settings, perform the following steps:
1. On the **Initialization Settings**, navigate to **Theme Settings** in your Shopify admin.
2. Turn the **MoEngage Shopify app embed** setting toggle on.
3. Click **Save**.
## Step 4: Configuration Settings
To configure the settings, perform the following steps:
1. Go to the **Configuration** tab.
2. Select the user events and properties you want MoEngage to track from your store. Some events are tracked by default. For more information, refer to [Events and user data tracking](/docs/developer-guide/ecommerce-platforms/shopify/events-and-user-data-tracking) article.
3. Select your user identifier: In the **User Identifier for user synced** box, click either **Email** or **Phone Number** by using the unique identifier (UID). MoEngage uses the UID to recognize and merge user profiles across sessions and devices.
**Warning** After setup is complete, you cannot change your user identifier. For most stores, the identifier most reliably present at checkout is the email address. If your store collects phone numbers at checkout and email is optional, choose a phone number. Changing the identifier after setup requires re-integrating the app and affects all existing user profiles in your workspace.
4. Click **Save configuration**.
## Step 5: Enable Web Personalization (Optional)
To enable web personalization, perform the following steps:
1. Confirm that Web Personalization is enabled for your MoEngage account. If it is not, contact your Customer Success Manager or the support team.
2. On the **Configuration** tab, turn the **Enable web personalization** **on your store** toggle on.
# Next Steps
Now that your integration is successful, you can sync data about your past orders and start tracking events.
1. [Events and User Data Tracking](/docs/developer-guide/ecommerce-platforms/shopify/events-and-user-data-tracking)
2. [Sync User Data](/docs/developer-guide/ecommerce-platforms/shopify/user-data-sync)
3. [Steps to Validate Integration](/docs/developer-guide/ecommerce-platforms/shopify/validate-integration)
4. [FAQs](/docs/developer-guide/ecommerce-platforms/shopify/faqs)
# Sync Your Product Catalog
Source: https://moengage.com/docs/developer-guide/ecommerce-platforms/shopify/sync-product-catalog
Sync your Shopify product catalog to MoEngage to generate personalized recommendations for customers.
When you enable catalog sync, MoEngage fetches your Shopify product data and makes it available for campaigns, flows, and personalization. This article describes the catalog sync operation, lists the data included in the sync, and provides guidance to maintain the catalog.
**Prerequisites**
Basic Recommendations must be enabled for your account before you use catalog sync for personalization. Accounts on the Enterprise plan have Basic Recommendations enabled by default. If you use a different plan, you can contact your Customer Success Manager or the MoEngage Support team to enable this feature.
## Catalog Sync Operation
MoEngage syncs your Shopify product catalog by fetching data from your store in real time. Once synced, your catalog serves as a data source for product recommendation blocks, dynamic content in messages, and segmentation filters.
Catalog sync is not enabled by default. To activate it, navigate to your Shopify integration settings in MoEngage and turn the **Catalog Sync** toggle on. Note that the toggle requires a one-time setup step on the MoEngage side before it takes effect.
**One time activation required**
Before catalog sync functions, the MoEngage team must activate them for your workspace. If products do not appear after you enable the toggle, you can contact your Customer Success Manager or raise a support ticket.
## Configure Catalog Sync
Before you request the first sync, define your catalog schema. You name the catalog, map each attribute to a Shopify source, choose the attribute that identifies an item uniquely, and select the currency for product prices.
To configure and start the catalog sync, perform the following steps:
1. In your Shopify admin, navigate to **Apps** > **MoEngage**.
2. Click the **Sync product catalog** tab.
3. In **Catalog name**, enter a name for your catalog.
4. For each attribute in the table, select a **Data type** and a **Shopify source**.
5. Select the **Mark unique** checkbox for the attribute that uniquely identifies each item in your catalog.
6. To map an attribute that the table does not already list, enter a name under **Add custom attributes**, select a **Data type**, and click **Add attribute**.
7. Under **Choose currency of product price**, select the currency your product prices use.
8. Click **Request catalog sync**.
9. In the **Confirm catalog sync** dialog, click **Confirm**.
MoEngage prefills the table with the default mapping described in [Synced Attributes](#synced-attributes). You can change the data type or the Shopify source of any prefilled attribute, or remove an attribute using the delete icon at the end of its row.
Each custom attribute you add appears as a new row at the end of the table. **Request catalog sync** stays disabled until every row has both a data type and a Shopify source.
The **Shopify source** list contains native product fields such as Product title, Description (HTML), Vendor / Brand, Tags, URL Handle, Variant Price, Compare at price, SKU, Inventory Qty, Variant name, and Primary Image URL. Your Shopify metafields also appear in this list, marked as **Meta field**.
### Choose the Unique Identifier
Every catalog needs one attribute that identifies each item uniquely. Select the **Mark unique** checkbox on that attribute's row. You can mark **Variant ID**, **SKU**, or a custom attribute as the unique identifier.
You cannot change the unique identifier after the first sync completes, so choose an attribute whose value is different for every item. If two or more items share the same value, MoEngage treats them as the same item and keeps only the most recent values it receives for that identifier, which means the earlier item is overwritten in your catalog.
### Configuration You Cannot Change After the First Sync
MoEngage fixes part of your schema once the first sync completes successfully, so review your mapping before you request the sync.
Once you sync, the currency and the attributes added so far cannot be edited or deleted. You can still add new attributes later.
After the first successful sync, you cannot rename or delete the attributes already in the table, change their data types, change their Shopify sources, or change the catalog currency. You can add new custom attributes at any time, and MoEngage includes them in the next sync.
## Sync Frequency
After the first sync completes, MoEngage keeps your catalog up to date in real time. When a product changes in Shopify, that change reflects in your MoEngage catalog without waiting for a scheduled refresh, so campaigns that rely on catalog attributes such as product price, image, or availability use current values.
Real-time catalog sync is in early access.
MoEngage syncs only the products whose Shopify status is **Active**. If a product's status changes from **Active** to any other value, MoEngage removes it from the catalog.
## Synced Attributes
MoEngage prefills the mapping table with the following product attributes. Before the first sync, you can change the data type or Shopify source of any of them, remove the ones you do not need, and add your own.
| Attribute | Description | Shopify Source Field |
| ---------------------------- | -------------------------------------------------------------------------------------------------------------------------------------- | --------------------- |
| **Product title** | The name of the product as it appears in your store. | Product title |
| **Variant ID** | The unique identifier for each product variant (size, color, and so on). This serves as the primary key for each item in your catalog. | Variant ID |
| **Product ID** | The unique identifier for the parent product. Multiple variation IDs can have the same product IDs. | Product ID |
| **Product URL** | The URL to access the item on your storefront. | Product URL |
| **Primary Image URL** | The URL of the primary product image associated with the variation ID. | Primary Image URL |
| **Vendor / Brand** | The name of the product vendor, brand, or supplier. | Vendor / Brand |
| **SKU (Stock Keeping Unit)** | This is your internal product code for this variant. | SKU |
| **Variant Name** | The name of the specific variant (for example: Blue / XL). | Variant Name |
| **Variant Price** | The listed price for this variant. | Variant Price |
| **Inventory Qty** | The available inventory for this variant present in your store. | Inventory Qty |
| **Description (HTML)** | The product description text. | Description (HTML) |
| **Created At** | The date you created the product in Shopify (ISO 8601 format). | Product creation date |
| **Updated At** | The date you last modified the product in Shopify (ISO 8601 format). | Last modified date |
## Excluded Data
The following data does not sync in the default catalog sync:
* **Third-party app fields**: Only native Shopify product fields and your Shopify metafields are available as sources.
## Essential Considerations for Sync and Personalization
To use your catalog data effectively, review the following information:
Webhook events, such as **Shopify - Order Placed** and **Shopify - Add to Cart**, do not include product or image URLs in their payloads. To use these attributes in campaign personalization, use catalog enrichment at send time. MoEngage looks up the relevant product in your synced catalog and attaches the attributes to the message. For more information, refer [here](/docs/user-guide/content/recommendations/getting-started/overview).
Field values must be consistent in type across your catalog for products to import correctly. For example, if the **Variant Name** field contains text strings for most products but numeric values for others, MoEngage skips the products with numeric values. You can review your Shopify data for inconsistent formats in the **SKU**, **Price**, and **Variant Name** fields before you sync.
## Verify the Catalog Sync
To confirm the sync completes correctly, perform the following steps:
1. In your MoEngage dashboard, navigate to **Content** > [**Catalog**](/docs/user-guide/content/recommendations/prerequisites/catalogs) and check the total product count.
2. Compare this count to your active product count in Shopify (Shopify admin > **Products**, filtering for active, non-draft items).
3. If the count is lower than expected, check your Shopify product data for fields with inconsistent values, particularly **Variant Name**.
4. If products are still missing despite consistent data, contact support with your workspace ID and the approximate number of missing products.
## Troubleshoot
To troubleshoot catalog issues, review the following table:
| Symptom | What to check |
| --------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Products do not appear after you enable catalog sync. | Catalog sync requires a one time activation. Confirm with your Customer Success Manager or raise a support ticket to verify activation for your workspace. |
| MoEngage excludes some products after the sync completes. | Check the following:
Products whose Shopify status is not **Active** do not sync.
Fields with inconsistent data types across products, for example text for some and numeric for others, do not import.
If the attribute you marked as unique holds the same value for more than one item, MoEngage treats those items as one and keeps only the most recent values, so the earlier item does not appear.
|
| The catalog does not include metafield values. | Map the metafield to an attribute in the **Sync product catalog** tab. Your metafields appear in the **Shopify source** list, marked as **Meta field**. Map them before the first sync, because you cannot change an attribute's source afterward. |
| The SKU does not serve as the unique product identifier. | Select the **Mark unique** checkbox on the **SKU** row before the first sync. MoEngage marks **Variant ID** as unique by default, and you cannot change the mapping after the first sync completes. |
**Information**
If you have questions about your Shopify integration, read our [FAQs](/docs/developer-guide/ecommerce-platforms/shopify/faqs).
# User Data Sync
Source: https://moengage.com/docs/developer-guide/ecommerce-platforms/shopify/user-data-sync
Sync your existing Shopify users and order history to MoEngage for segmentation and engagement.
After you complete the initial integration, you can sync your existing Shopify users and their historical order data to MoEngage. This process allows you to build segments, trigger campaigns, and personalize communication based on past user behavior from the moment you start using MoEngage.
## Data Available for Sync
The synchronization process imports two primary data types into MoEngage:
| Data Type | Description |
| ----------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Order history** | MoEngage ingests past orders within your selected date range as individual [`Shopify - Order Placed`](/docs/developer-guide/ecommerce-platforms/shopify/events-and-user-data-tracking) events. Each event uses the original order timestamp from Shopify. |
| **User profiles** | MoEngage imports all registered users in your Shopify store. The system creates or updates profiles using the unique identifier (email or mobile number) you selected during integration setup. |
### User Attribute Mapping
MoEngage synchronizes the following attributes to the user profile if they are available in the Shopify record:
* Email
* First name
* Last name
* Phone
* City
* Country
* State
* Shopify ID
* Accepts marketing
* Order count
**Excluded Data**
By default, MoEngage does not sync Shopify customer metafields (custom data fields). If your personalization requires metafield data, refer to the [Events and User Data Tracking](/docs/developer-guide/ecommerce-platforms/shopify/events-and-user-data-tracking) article for custom integration steps.
## Sync Prerequisites
### Event Retention Period
To ensure your event retention settings accommodate the data range you intend to sync, perform the following check before you begin the synchronization process:
MoEngage stores events for a specific retention window. If your selected sync range exceeds your retention window, MoEngage stops syncing orders once it reaches the limit. The sync status will show as "Sync successful," but older data will be missing from the dashboard.
**Warning**
If you plan to sync more than 60 days of history, contact your Customer Success Manager or raise a support ticket to increase your event retention period **before** starting the sync. You cannot apply these changes retroactively.
### User Identifiers
MoEngage synchronizes users and their orders based on the unique identifier you selected during integration setup:
* If you selected **email**, the system silently excludes users who do not have an email address.
* The system does not generate error messages for skipped records.
## Sync Process
To synchronize your Shopify data, perform the following steps:
1. Open your MoEngage Shopify app and click the **Sync Shopify data** tab.
2. Select the date range for your order history. You can sync data for up to the last 2 years from your installation date.
3. Click **Sync past data**.
Monitor progress in the **Sync progress** section.
## Sync Verification
**Sync status does not confirm data arrival**
The sync status might update to **Sync successful** within seconds of starting. This indicates the request was received, not that the data has finished processing. Large datasets can take several hours, and up to 24 hours for very large stores. Always verify directly on user profiles.
To verify the sync, perform the following steps:
1. Wait for at least two hours after starting the sync.
2. In the MoEngage UI, navigate to a user profile for a customer who placed an order within the sync window.
3. Verify that the `Shopify - Order Placed` event appears on their profile with the correct original order date.
4. If the sync status shows as successful but no data appears after 24 hours, contact support with your selected date range and approximate order count.
## Automatic Event Updates
After the historical sync completes, MoEngage automatically tracks new orders via Shopify webhooks. You do not need to run the sync again for new data.
**Important**
The sync is a one-time operation for historical backfill. Do not run the sync a second time for the same date range, as this action creates duplicate events in MoEngage.
## Synced User Attributes
MoEngage creates a `Shopify - Order Placed` event for each unique order ID within your selected date range. The `Created at` attribute on each event reflects the date and time the order was originally placed in Shopify.
For more information on the list of event attributes, refer to the [Events and User Data Tracking](/docs/developer-guide/ecommerce-platforms/shopify/events-and-user-data-tracking) article.
# User Profile Management with Shopify
Source: https://moengage.com/docs/developer-guide/ecommerce-platforms/shopify/user-profile-management-with-shopify
Understand how user profiles transition from anonymous to registered and how merging works with Shopify.
A typical journey of a user visiting a Shopify store transitions from an anonymous visitor to a known user and finally to a registered customer. This article describes how users transition through these stages, how their actions reflect in MoEngage user profiles, and the steps to take when duplicate profiles appear.
## Shopify User Tracking
A user visiting your store is tracked by two primary sources:
* **MoEngage Web SDK**: Integrated with your store as part of the initial setup.
* **Webhooks**: Real-time event notifications sent directly from Shopify to MoEngage after the integration is complete.
**Information**
When both sources track the same event, use the webhook version for segmentation and campaign creation. Webhook events are received directly from Shopify and provide the most reliable signal for campaign logic
## User Profile Attribute Capture
Profile attributes such as first name, last name, and Shopify ID are tracked when a user creates an account. For anonymous users, these attributes are collected during checkout along with the checkout updated event.
Email addresses and mobile numbers are captured when a user submits an on-site messaging form, logs in, registers, or provides contact details during checkout. MoEngage uses the captured value as the unique identifier (UID) to recognize and merge profiles.
**UID selection is permanent**
The identifier you choose during integration (email or mobile number) cannot be changed after setup without significant consequences for your entire user database. Verify which identifier is most reliably present at checkout before you complete the installation.
## Profile Merge
Profile merge occurs when MoEngage finds two profiles with the same UID value. This process ensures user activity remains under a single profile when a customer visits from a new device.
In Shopify integrations, MoEngage uses the **Cart token** as a common identifier to merge SDK and Webhook events. This token is generated when the first item is added to a cart and refreshed each time the cart is emptied.
## Duplicate Profile Information
You might notice more user profiles in MoEngage than visitors reported in Shopify analytics. To troubleshoot profile count discrepancies, review the following scenarios:
* **Buy Now Button usage**: If your site allows checkout without adding a product to the cart, SDK and webhook events cannot merge, which creates extra profiles.
* **Missing UID at checkout**: If a user places an order from a new device without entering the UID attribute, MoEngage creates a separate profile that cannot be merged automatically.
To prevent these tracking discrepancies from creating persistent duplicate records, you can implement [MoEngage's Identity Resolution](#identity-resolution) to automatically reconcile and link user profiles.
## Identity Resolution
If your store has persistent duplicate profile issues, enable Identity Resolution. Identity Resolution is a MoEngage feature that links user profiles from multiple sources into a single unified profile. This allows MoEngage to recognize a user using more than one identifier, such as both email and mobile number.
When you activate Identity Resolution, a one-time merge runs on all existing duplicate profiles based on your configured identifiers. Existing users merge over 48 hours, while new users merge immediately.
**Profile merges are irreversible**
After two profiles merge, they cannot be unmerged. Review your identifier configuration and merge rules carefully before you activate this feature.
## Identity Resolution Enablement
To enable Identity Resolution for your Shopify workspace, perform the following steps:
1. Contact your Customer Success Manager to request enablement for your workspace.
2. After enabled, go to **Settings** > **Data** > **Identity Resolution** in your MoEngage dashboard to configure identifiers and merge rules.
For more information on configuration steps, refer to the [Unified Identity (Identity Resolution)](/docs/user-guide/data/user-data/unified-identity-identity-resolution).
## Webhook Profile Resolution Logic
MoEngage creates a new profile for a webhook event only when the system cannot resolve the event to an existing user. This ensures that every event is captured and that campaigns depending on those events trigger correctly. If you observe a high volume of new profiles from webhook events, enabling Identity Resolution is the most effective solution.
# Validate Your Shopify Integration
Source: https://moengage.com/docs/developer-guide/ecommerce-platforms/shopify/validate-integration
Validate your MoEngage Shopify integration to ensure user events and data are tracked correctly.
After you install MoEngage on your Shopify store, validate the integration to confirm that MoEngage tracks user events and data correctly before you go live with campaigns. This article describes how to verify the SDK load, confirm that key events trigger, and explains what to expect if your store uses a customized Shopify theme or a third-party checkout partner.
## Prerequisites
Before you begin the validation process, ensure you have the following items:
* The [**MoEngage Assist** **extension**](https://chromewebstore.google.com/detail/moengage-sdk/dhggnkfnnoebbfofpimfehcklnekmbgi) is installed in your browser.
* Access to your live Shopify storefront.
* Your MoEngage workspace should be open in a separate tab to cross-check event ingestion.
## Step 1: Verify the MoEngage SDK Load
To verify the SDK load, perform the following steps:
1. Open your Shopify store in a new browser window, or perform a hard refresh on an existing window.
2. Open the **MoEngage Assist** Chrome extension on your storefront. If the SDK loads successfully, the extension icon turns blue and shows a green **Live** status. Click the icon to open the panel, which displays **No Issues Found** under the **Issues** tab.
To resolve SDK load failures, perform the following checks:
* In your Shopify admin, open **Apps** > **MoEngage** > **App embed settings**. Un-save and then re-save the app embed block. This action re-triggers the script injection and resolves most cases where the SDK fails to load.
* If the icon is still not blue after you re-save, open the browser developer console and check for JavaScript errors on the page. SDK load failures appear here.
* If the issue persists, contact MoEngage support.
**Inconsistent setup steps can prevent SDK from loading**
The SDK does not load if you do not complete the integration steps in order. Before you raise a support ticket, confirm that you follow all steps in the Shopify setup article, including the save of the app embed block in your Shopify theme settings.
## Step 2: Validate Event Track
To trigger and confirm events, perform the following steps:
To validate the **Product Viewed** event, perform the following steps:
1. Navigate to any product page on your storefront.
2. Confirm that the **Product Viewed** event appears in the **MoEngage Assist** extension event stream under the **Track** tab with a green **Tracked** status.
3. Click the drop-down arrow on the event row inside the extension to expand and verify that key attributes—such as price, currency, and product ID—are tracked with correct values.
**Product Viewed only triggers on default product template**
This event tracks by using the default Shopify product page template. If your store uses custom product page templates, the event is not triggered on those pages by default.
To validate the **Add to Cart** event, perform the following steps:
1. From any product page, add an item to your cart.
2. Confirm that the **Shopify - Add to Cart** event appears in the **MoEngage Assist** panel.
**Which Add to Cart event is used in campaigns?**
MoEngage tracks **Add to Cart** via both the Web SDK and a Shopify webhook. These two events have different names, and their counts do not match. Shopify counts sessions that result in a cart creation, while MoEngage counts the number of times the **Add to Cart** action occurs. Always use the webhook version (**Shopify - Add to Cart**) for segment and campaign triggers. For more information, refer to the [**Events and User Data Track**](/docs/developer-guide/ecommerce-platforms/shopify/events-and-user-data-tracking) article.
## Verify Cart Token User Attribute
To verify the cart token, perform the following steps:
1. While you validate **Product Viewed** and **Add to Cart**, confirm that the **cart\_token** user attribute is set on the user profile.
2. Verify that the **cart\_token** user attribute is listed and marked as **Tracked** inside your browser extension attributes dashboard. The cart token is required for abandoned cart flows to function correctly.
## Third-Party Checkout Partners
To manage event track for checkout partners, review the following information:
MoEngage tracks checkout events by using the Shopify default checkout process. If your store uses a third-party checkout partner—such as **GoKwik**, **Razorpay Magic Checkout**, or a similar accelerated checkout tool—the behavior below occurs:
| Scenario | What happens |
| ---------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| SDK events after checkout initiate | Shopify does not allow Web SDK events to trigger after a third-party checkout flow initiates. **On-Site Messaging (OSM)** campaigns that depend on SDK events cannot trigger at or after checkout. |
| Checkout page URL in events | Where there is no URL, the **checkout page URL** attribute is absent from related events. |
| Add to Cart SDK event | If a third-party checkout partner intercepts the cart action, the **Add to Cart** SDK event might not trigger. The webhook version of the event is unaffected. |
**Webhook-based events unaffected**
**Order Placed** and other webhook-based events continue to track normally regardless of which checkout partner you use. This impact is limited to SDK-based event track at and after the checkout stage.
## Heavily Customized Shopify Themes
To manage event track on custom themes, review the following information:
The MoEngage Web SDK integrates with Shopify's default storefront workflow. Heavily customized Shopify themes sometimes modify this workflow, preventing events from firing even when the SDK loads successfully. If events do not appear in the plugin, check whether your store uses a custom theme that overrides standard Shopify page templates or cart behavior. If it does, contact support to determine the additional implementation required.
**Validate on your live storefront**
If your store uses a heavily customized theme, validate event track on the live storefront before you launch campaigns. Custom theme behavior in a development preview can differ from the published store.
## Troubleshoot: If Events Do Not Appear In the Plugin
To troubleshoot event track issues, perform the following steps in order before you raise a support ticket:
1. Confirm that the MoEngage app is installed on the correct Shopify store and that the workspace ID entered during setup matches your active MoEngage workspace.
2. If you recently generated new API keys, wait 10 minutes before you test. Newly generated keys take up to 10 minutes to activate.
3. Check that the app embed block is saved in your Shopify theme settings. Go to **Apps** > **MoEngage** > **App embed settings**, un-save and re-save the embed block, and then test again.
4. Check whether your store uses a custom theme or a third-party checkout partner and review the relevant sections on this page: [**Third-Party Checkout Partners**](#third-party-checkout-partners) and [**Heavily Customized Shopify Themes**](#heavily-customized-shopify-themes).
5. Open the browser developer console on the product or cart page and check for JavaScript errors. SDK load failures appear here.
If none of the above actions resolve the issue, contact MoEngage support. Include your workspace ID, the specific event that is not firing, the test page URL, and whether your store uses a custom theme or a third-party checkout partner.
For more information, see the [FAQs](/docs/developer-guide/ecommerce-platforms/shopify/faqs).
# WooCommerce
Source: https://moengage.com/docs/developer-guide/ecommerce-platforms/woo-commerce/woocommerce
Integrate your WooCommerce store with MoEngage using Google Tag Manager for event and user tracking.
WooCommerce is an open source e-commerce platform built on WordPress, that is used by more than 3M+ online stores worldwide. This document covers the integration steps required to integrate a store on WooCommerce with Moengage.
* Add Google Tag Manager (GTM) to your WooCommerce store. Refer to this [official documentation](https://woocommerce.com/document/gtm-ecommerce-woo-pro/#how-to-install) for detailed steps.
* Complete the GTM and MoEngage integration by following the integration steps [here](https://partners.moengage.com/hc/en-us/articles/18795331766676).
* You can track any user event and/or User properties from your store to MoEngage via GTM.
* This integration allows you to further integrate with the following MoEngage channels.
| Channel | Supported? |
| ------------------- | ------------------------------------------------------------------------------------------------------------------------ |
| On- site messaging | Yes |
| Web personalization | Yes ([integration](/docs/developer-guide/personalize-sdk/sdk-integration/web-personalization-v2) script should be added) |
| WebPush | No |
Your store must be on WooCommerce Pro plan to complete this integration.
# Self Handled Cards
Source: https://moengage.com/docs/developer-guide/flutter-sdk/cards/self-handled-cards
Build custom card views in your Flutter app using the MoEngage self-handled cards SDK and APIs.
Self-handled cards give you the flexibility of creating Card Campaigns on the MoEngage Platform and displaying the cards anywhere inside the application. SDK provides APIs to fetch the campaign's data using which you can create your own view for cards.

# SDK Installation
# Installation
To add MoEngage Cards SDK to your application, edit the application's **pubspec.yaml** file and add the below dependency to it:
```yaml pubspec.yaml theme={null}
dependencies:
moengage_cards: $latestVersion
```
***\$latestVersion*** refers to the latest version of the plugin.
Post including the dependency, run ***flutter pub get*** command in the terminal to install the dependency.
After installing the plugin, use the following platform-specific configuration.
This plugin is dependent on **moengage\_flutter** plugin. Make sure you have installed the **moengage\_flutter** plugin as well. Refer to the [documentation](/docs/developer-guide/flutter-sdk/sdk-integration/sdk-installation/framework-dependency) for the same.
# Android Installation
Add the following dependency in the *app/build.gradle* file.
```json build.gradle wrap theme={null}
dependencies {
...
implementation("com.moengage:cards-core:$sdkVersion")
}
```
replace **\$sdkVersion** with the appropriate SDK version. Minimum supported version 1.5.0.
# iOS Installation
In the case of iOS, the native dependency is part of the Cards flutter SDK itself, so there is no need to include any additional dependency for supporting Cards.
# Initialize Cards
MoEngage Cards module can be initialized in the widget where the cards module is being used.
```Dart Dart theme={null}
MoEngageCards cards = MoEngageCards(YOUR_WORKSPACE_ID);
cards.initialize();
```
Example
```Dart Dart theme={null}
// Use Named Import otherwise MoEngage classes might be collided with classed in flutter/material.dart
import 'package:moengage_cards/moengage_cards.dart' as moe;
class CardsScreen extends StatefulWidget {
const CardsScreen({Key? key}) : super(key: key);
@override
State createState() => _CardsScreenState();
}
class _CardsScreenState extends State{
moe.MoEngageCards cards = moe.MoEngageCards("MOE_Workspace_ID");
@override
void initState() {
super.initState();
cards.initialize();
}
}
```
# Get Cards Info
Fetch All the cards campaign data that are eligible to show for the particular user which returns data as ***CardsInfo***. For a complete list of data models please refer to the [API documentation](https://pub.dev/documentation/moengage_cards/).
```Dart Dart theme={null}
MoEngageCards cards = MoEngageCards(YOUR_WORKSPACE_ID);
CardsInfo cardsInfo = await cards.getCardsInfo();
```
## Widget and Widget Id Mapping
### Basic Card/Illustration Card
| Widget Id | Widget Type | Widget Information |
| --------- | -------------------------- | --------------------------------- |
| 0 | Image (WidgetType.IMAGE) | Image widget in the card. |
| 1 | Text (WidgetType.TEXT) | Header text for the card. |
| 2 | Text (WidgetType.TEXT) | Message text for the card. |
| 3 | Button (WidgetType.Button) | Call to action(CTA) for the card. |
# Refresh Cards
Use the ***refreshCards***\*()\*\*\* API to refresh cards on the User Demand. This API can be used to mimic Pull to refresh behavior.
```Dart Dart theme={null}
MoEngageCards cards = MoEngageCards(YOUR_WORKSPACE_ID);
cards.refreshCards((data) { if (data?.hasUpdates == true) { // Update UI }});
```
# Fetch Cards
Use the ***fetchCards***\*()\*\*\* API to fetch cards for the User. This API can be used to sync latest cards data.
```Dart Dart theme={null}
MoEngageCards cards = MoEngageCards(YOUR_WORKSPACE_ID);
cards.fetchCards().then((data) { // Update UI});
```
For details on the sync timing and rate limits for `fetchCards()`, see [When Does the MoEngage SDK Sync Card Data?](/docs/user-guide/campaigns-and-channels/cards/faqs-cards/when-does-the-moengage-sdk-sync-card-data)
# Inbox Loaded
You can show the cards on a separate screen or a section of the screen. When the cards screen/section is loaded call ***onCardsSectionLoaded()***.
```Dart Dart theme={null}
MoEngageCards cards = MoEngageCards(YOUR_WORKSPACE_ID);
cards.onCardsSectionLoaded((data) {
if (data?.hasUpdates == true) {
// Refresh UI
}
});
```
# Inbox UnLoaded
Call ***onCardSectionUnloaded()*** when the screen/section is no longer visible or going to the background.
```Dart Dart theme={null}
MoEngageCards cards = MoEngageCards(YOUR_WORKSPACE_ID);
cards.onCardsSectionUnLoaded();
```
# Fetch Categories
To fetch all the categories for which cards are configured, use the ***getCardsCategories()*** API.
```Dart Dart theme={null}
MoEngageCards cards = MoEngageCards(YOUR_WORKSPACE_ID);
List categories = await cards.getCardsCategories();
```
# All Cards Categories Enabled
To fetch all the categories for which cards are configured, use the ***isAllCategoryEnabled()*** API.
```Dart Dart theme={null}
MoEngageCards cards = MoEngageCards(YOUR_WORKSPACE_ID);
bool isAllCategoryEnabled = await cards.isAllCategoryEnabled();
```
# Fetch Cards for Categories
To fetch cards eligible for display for a specific category use the ***getCardsForCategory()*** API.
```Dart Dart theme={null}
MoEngageCards cards = MoEngageCards(YOUR_WORKSPACE_ID);
int count = await cards.getCardsForCategory(category);
```
# Get New Cards Count
To obtain the new cards count use ***getNewCardsCount()*** method as shown below:
```Dart Dart theme={null}
MoEngageCards cards = MoEngageCards(YOUR_WORKSPACE_ID);
int count = await cards.getNewCardsCount();
```
# Card Shown
Call the ***cardShown()*** API to notify a card is shown to the user.
```Dart Dart theme={null}
MoEngageCards cards = MoEngageCards(YOUR_WORKSPACE_ID);
cards.cardShown(context, card); // Pass Card Object
```
# Card Clicked
Call the ***cardClicked()*** API to notify a card is shown to the user.
```Dart Dart theme={null}
MoEngageCards cards = MoEngageCards(YOUR_WORKSPACE_ID);
cards.cardClicked(card, widgetId); // Pass Card Object
```
# Delete Card
Call the ***deleteCard()*** API to delete a card.
```Dart Dart theme={null}
MoEngageCards cards = MoEngageCards(YOUR_WORKSPACE_ID);
cards.deleteCard(card); // Pass Card Object
```
# Mark Card Delivered
To track delivery to the card section of the application call the ***cardDelivered()*** API when the cards section of the application is loaded.
```Dart Dart theme={null}
MoEngageCards cards = MoEngageCards(YOUR_WORKSPACE_ID);
cards.cardDelivered();
```
# Delete Multiple Cards
Call the ***deleteCards()*** API to delete a card.
```Dart Dart theme={null}
MoEngageCards cards = MoEngageCards(YOUR_WORKSPACE_ID);
cards.deleteCards(context, cards); // Pass List of Cards
```
# Get Unclicked Cards Count
To obtain the unclicked cards count use ***getUnClickedCardsCount()*** method as shown below.
```Dart Dart theme={null}
MoEngageCards cards = MoEngageCards(YOUR_WORKSPACE_ID);
int count = await cards.getUnClickedCardsCount();
```
# App Open Card Sync Listener
Set this listener to get a callback for card sync on the App opened. This listener should be set before calling ***initialize()*** API. In most cases, this API is not required.
```Dart Dart theme={null}
MoEngageCards cards = MoEngageCards(YOUR_WORKSPACE_ID);
cards.setAppOpenCardsSyncListener((data) {
//Update UI
});
cards.initialize();
```
The hybrid framework does not support the MoEngage default Card. Only the Self-handled Card is supported.
# Compliance
Source: https://moengage.com/docs/developer-guide/flutter-sdk/compliance/compliance
Enable or disable data tracking and the MoEngage Flutter SDK from Dart.
Use the APIs below to control what the MoEngage SDK tracks, based on the consent a user has given.
## Enable or Disable Data Tracking
To stop the SDK from tracking custom events and user attributes, call `disableDataTracking()`.
```dart Dart wrap theme={null}
import 'package:moengage_flutter/moengage_flutter.dart';
final MoEngageFlutter _moengagePlugin = MoEngageFlutter();
_moengagePlugin.disableDataTracking();
```
The SDK rejects all events and user attributes until you call `enableDataTracking()`. Data tracking is enabled by default.
```dart Dart wrap theme={null}
import 'package:moengage_flutter/moengage_flutter.dart';
final MoEngageFlutter _moengagePlugin = MoEngageFlutter();
_moengagePlugin.enableDataTracking();
```
## Enable or Disable the SDK
To stop the SDK from tracking any user information or sending any data to MoEngage, call `disableSdk()`.
```dart Dart wrap theme={null}
import 'package:moengage_flutter/moengage_flutter.dart';
final MoEngageFlutter _moengagePlugin = MoEngageFlutter();
_moengagePlugin.disableSdk();
```
All SDK APIs are non-operational until you call `enableSdk()`. The SDK is enabled by default, so call `enableSdk()` only if you disabled it earlier.
```dart Dart wrap theme={null}
import 'package:moengage_flutter/moengage_flutter.dart';
final MoEngageFlutter _moengagePlugin = MoEngageFlutter();
_moengagePlugin.enableSdk();
```
## Delete User Data
To delete the current user's profile from the MoEngage server, refer to [Delete User From MoEngage Server](/docs/developer-guide/flutter-sdk/data-tracking/delete-user-from-moengage-server).
# Delete User From MoEngage Server
Source: https://moengage.com/docs/developer-guide/flutter-sdk/data-tracking/delete-user-from-moengage-server
Delete the current user from the MoEngage server using the deleteUser() method in the Flutter SDK.
This API is supported from **moengage\_flutter** version **6.1.0** and is only available for the Android platform and it will throw [UnImplementedError](https://api.flutter.dev/flutter/dart-core/UnimplementedError-class.html) error in other platforms
To delete the current user from the MoEngage server use the ***deleteUser()*** method as shown below, where you will get an instance of [***UserDeletionData***](https://pub.dev/documentation/moengage_flutter_platform_interface/latest/moengage_flutter_platform_interface/UserDeletionData-class.html).
```javascript Dart theme={null}
import 'package:moengage_flutter/moengage_flutter.dart';
final MoEngageFlutter _moengagePlugin = MoEngageFlutter();
_moengagePlugin.initialise();
// Below method will return an instance of >
_moengagePlugin.deleteUser().then((value) {
// Add your code to handle the callback.
}).catchError((onError) {
// Add your code to handle the Error.
});
```
For more information, please refer to the [API documentation](https://pub.dev/documentation/moengage_flutter/6.1.0/moengage_flutter/MoEngageFlutter/deleteUser.html).
# Enable Advertising Identifier Tracking
Source: https://moengage.com/docs/developer-guide/flutter-sdk/data-tracking/enable-advertising-identifier-tracking
Enable advertising identifier tracking in your Flutter app for accurate device analytics.
For accurate analytics around devices and tracking Re-installs, it is essential to track the Advertising Identifier.
## Add Ad Identifier Library
Add the below dependency in the application level ***build.gradle*** file.
```groovy Groovy theme={null}
implementation("com.google.android.gms:play-services-ads-identifier:18.0.1")
```
To enable Advertising Identifier tracking use the *enableAdIdTracking()* method as shown below.
```dart Dart theme={null}
import 'package:moengage_flutter/moengage_flutter.dart';
final MoEngageFlutter _moengagePlugin = MoEngageFlutter(YOUR_WORKSPACE_ID);
_moengagePlugin.initialise();
_moengagePlugin.enableAdIdTracking();
```
Before you enable Advertising Id tracking please ensure the application is complying with the [Google Play Policy](https://support.google.com/googleplay/android-developer/answer/10144311) regarding Advertising Id tracking. Refer to our [help document](https://www.moengage.com/docs/user-guide/data/privacy/android-advertising-id-tracking) for more information on the policy.
In case, you need to disable advertising-id after enabling tracking use the following method.
```dart Dart theme={null}
import 'package:moengage_flutter/moengage_flutter.dart';
final MoEngageFlutter _moengagePlugin = MoEngageFlutter(YOUR_WORKSPACE_ID);
_moengagePlugin.initialise();
_moengagePlugin.disableAdIdTracking();
```
The above APIs are available only starting plugin version 4.2.0. In the older versions, Advertising Identifier tracking is enabled by default.
# Install/Update Differentiation
Source: https://moengage.com/docs/developer-guide/flutter-sdk/data-tracking/install-update-differentiation
Differentiate between app installs and updates in your Flutter app using the MoEngage setAppStatus API.
SDK needs support to enable the update by the user application or install the application. You need to have logic on the app side to distinguish between app *INSTALL* and *UPDATE*.
If the user was already using your application and has just updated to a new version that has MoEngage SDK, it is an update. Call the below API
```Dart Dart theme={null}
import 'package:moengage_flutter/moengage_flutter.dart';
final MoEngageFlutter _moengagePlugin = MoEngageFlutter(YOUR_WORKSPACE_ID);
_moengagePlugin.initialise();
_moengagePlugin.setAppStatus(MoEAppStatus.update);
```
In case it is a fresh install call the below API
```Dart Dart theme={null}
import 'package:moengage_flutter/moengage_flutter.dart';
final MoEngageFlutter _moengagePlugin = MoEngageFlutter(YOUR_WORKSPACE_ID);
_moengagePlugin.initialise();
_moengagePlugin.setAppStatus(MoEAppStatus.install);
```
For more information, refer to [Flutter SDK](https://github.com/moengage/Flutter-SDK).
# Setting Unique Id for SDK versions below 9.2.0
Source: https://moengage.com/docs/developer-guide/flutter-sdk/data-tracking/setting-unique-id-for-sdk-versions-below-920
Set a unique user ID for login and logout handling in MoEngage Flutter SDK versions below 9.2.0.
# Implementing Login/Logout
* It's important to set the User Attribute Unique ID when a user logs into your app.
* This is to merge the new user with the existing user, if any exists, and will help prevent creation of unnecessary/stale users.
* Setting the Unique ID is a critical piece to tie a user across devices and installs/uninstalls as well across all platforms (i.e. iOS, Android, Windows, The Web). Set the **USER\_ATTRIBUTE\_UNIQUE\_ID** attribute as soon as the user is **logged in**. Unique ID can be something like an email ID, a username (unique), or a database ID or any Backend generated ID.
* Do not set this for the user who not logged in.
## Login
```Dart Dart theme={null}
import 'package:moengage_flutter/moengage_flutter.dart';
final MoEngageFlutter _moengagePlugin = MoEngageFlutter(YOUR_WORKSPACE_ID);
_moengagePlugin.initialise();
_moengagePlugin.setUniqueId("Unique ID");
```
**Note:** The following values are not allowed in the UniqueID field: "unknown", "guest", "null", "0", "1", "true", "false", "user\_attribute\_unique\_id", "(empty)", "na", "n/a", "", "dummy\_seller\_code", "user\_id", "id", "customer\_id", "uid", "userid", "none", "-2", "-1", "2"
## Logout
The application needs to notify the MoEngage SDK whenever the user is logged out of the application. To notify the SDK, call the API whenever the user is logged out of the application.
```Dart Dart theme={null}
import 'package:moengage_flutter/moengage_flutter.dart';
final MoEngageFlutter _moengagePlugin = MoEngageFlutter(YOUR_WORKSPACE_ID);
_moengagePlugin.initialise();
_moengagePlugin.logout();
```
## Updating User Attribute Unique Id
Use the method *setAlias()* to update the user attribute unique id instead of *setUniqueId()* with a different value. Using the method *setUniqueId()* with a new value creates unintended users in MoEngage.
```Dart Dart theme={null}
import 'package:moengage_flutter/moengage_flutter.dart';
final MoEngageFlutter _moengagePlugin = MoEngageFlutter(YOUR_WORKSPACE_ID);
_moengagePlugin.initialise();
_moengagePlugin.setAlias("Updated Unique ID");
```
**Critical**
Please make sure that you use `setAlias()` for updating the Unique Identifier and not `setUniqueId()` as calling `setUniqueId()` with a new value will reset the current user and lead to the creation of unintended users in our system.
# Tracking Events
Source: https://moengage.com/docs/developer-guide/flutter-sdk/data-tracking/tracking-events
Track custom user events and their properties in your Flutter app using the MoEngage trackEvent API.
SDK adheres to the MoEngage FUP policies. For more information, refer to the [Fair Usage Policy](https://www.moengage.com/docs/user-guide/data/key-concepts/fair-usage-policy-fup).
Tracking events is how you record any actions your users perform, along with any properties that describe the action. Every trackEvent call records a single user action. We recommend that you make your event names human-readable so that everyone on your team can know what they mean instantly.
Every TrackEvent() call expects 2 parameters, event name, and Properties instance which represent additional event attributes about the event. Add all the additional information which you think would be useful for segmentation while creating campaigns. For eg: the following example shows an example of tracking an event with all the possible data types.
```Dart Dart theme={null}
import 'package:moengage_flutter/moengage_flutter.dart';
var properties = MoEProperties();
properties.addAttribute("attrString", "String Value")
.addAttribute("attrInt", 123)
.addAttribute("attrBool", true)
.addAttribute("attrDouble", 12.32)
.addAttribute("attrLocation", new MoEGeoLocation( 12.1, 77.18) )
.addAttribute("attrArray", ["item1", "item2", "item3"])
.addAttribute('product', {'item-id' : 123,'item-type' : 'books','item-cost' : {'amount' : 100,'currency' : 'USD'}})
.addAttribute('products', [{'item-id' : 123,'item-cost' : {'amount' : 100,'currency' : 'USD'}},{'item-id' : 323,'item-cost' : {'amount' : 90,'currency' : 'USD'}}])
.addISODateTime("attrDate", "2019-12-02T08:26:21.170Z");
final MoEngageFlutter _moengagePlugin = MoEngageFlutter(YOUR_WORKSPACE_ID);
_moengagePlugin.initialise();
_moengagePlugin.trackEvent('Flutter Event', properties);
```
You can not use "moe\_" as a prefix while naming events, event attributes, or user attributes. It is a system prefix and using it might result in periodic blacklisting without prior communication.
# Analytics
MoEngage SDK has started tracking user sessions and application traffic sources. To learn more about how user session and application traffic source tracking works, refer to the following docs:
* [Session and Source Analysis](https://www.moengage.com/docs/user-guide/analyze/analytics/session-and-source/session-and-source-analysis)
* [Advanced Session and Source Analysis](https://www.moengage.com/docs/user-guide/analyze/analytics/session-and-source/advanced-session-and-source-analysis)
With user session tracking we have introduced the flexibility to selectively mark events as non-interactive.
## What is a non-interactive event?
Events that do not affect the session calculation in anyways are called non-interactive events. Non-interactive events have the below properties
* Do not start a new session.
* Do not extend the session.
* Do not have information related to a user session.
## How to mark an event as non-interactive?
To mark an event as a non-interactive call **setNonInteractiveEvent()** for **Properties** instance as shown below:
```Dart Dart theme={null}
import 'package:moengage_flutter/moengage_flutter.dart';
var properties = MoEProperties();
properties.addAttribute( "attrString", "String Value")
.addAttribute("attrInt", 123)
.setNonInteractiveEvent();
final MoEngageFlutter _moengagePlugin = MoEngageFlutter(YOUR_WORKSPACE_ID);
_moengagePlugin.initialise();
_moengagePlugin.trackEvent('Non Interactive Event', properties);
```
# Tracking User Attributes and User Identity
Source: https://moengage.com/docs/developer-guide/flutter-sdk/data-tracking/tracking-user-attributes-and-user-identity
Track user attributes and set identifiers in the MoEngage Flutter SDK for cross-platform identification.
User attributes are pieces of information you know about a user. They could be demographics such as age and gender, account-specific like plan, or whether a user has seen a particular A/B test variation. User attributes are customer properties you can reference throughout the customer's lifecycle.
## Difference Between User Attributes and User Identifiers
User attributes and user identifiers serve different purposes in MoEngage:
**User Identifiers:**
User identifiers are unique values that persist across multiple sessions and devices, allowing MoEngage to recognise a user as the same individual, even when they switch between different platforms or log in later. This process, known as identity resolution, is crucial for maintaining a unified user profile, providing a consistent user experience, and tracking user behaviour accurately.
Common examples of user identifiers include:
* Email address: A user's email address is a widely used identifier because it is unique to the individual and remains consistent across different platforms.
* Phone number: Similar to email addresses, phone numbers can serve as unique identifiers, especially in mobile applications.
* User ID: MoEngage assigns each user a unique ID upon registration. This ID is used as a reliable identifier within the MoEngage platform.
* Customer ID: In e-commerce and customer relationship management (CRM) systems, a customer ID is assigned to track individual customers across various interactions.
These identifiers are set using the ***identifyUser()*** method.
By default, parameter ***ID*** is the identifier used for your workspaces, unless [Identity resolution](https://www.moengage.com/docs/user-guide/data/user-data/unified-identity-identity-resolution#overview) is enabled and identifiers are activated in your workspace.
**User Attributes**:
Descriptive information about a user that enhances their profile - Used for segmentation, personalisation, and analytics - Examples: name, age, gender, preferences, purchase history - Set using dedicated methods like ***setFirstName()*** or ***setUserAttribute()*** - Help create personalised user experiences
In simple terms, identifiers answer "Who is this user?" while attributes answer "What do we know about this user?"
## Powering MoEngage Features
User attributes and identifiers are crucial for leveraging MoEngage effectively:
* **Segmentation:**
* Use attributes to create targeted user groups based on demographics, behavior, etc.
* Example: Segment users by age, purchase history for specific campaigns.
* **Personalisation:**
* Identifiers ensure consistent user experience across devices.
* Attributes enable tailored content (messages, recommendations).
* Example: Personalise emails with names, recommend relevant products.
* **Analytics:**
* Attributes provide context to user actions and behavior data.
* Analyze conversion rates by segments, feature engagement by demographics.
* Gain deeper insights for data-driven decisions.
By using attributes and identifiers, you can build more relevant and engaging user experiences.
# Implementing Login/Logout
For SDK versions below 9.2.00 refer to [this](/docs/developer-guide/flutter-sdk/data-tracking/setting-unique-id-for-sdk-versions-below-920) document.
## Login User
**Single Identifier**
If your application relies on a single unique user identifier, such as an email ID for login, use the API below to pass the identifier to the MoEngage SDK
```dart Dart wrap theme={null}
import 'package:moengage_flutter/moengage_flutter.dart';
final MoEngageFlutter _moengagePlugin = MoEngageFlutter();
_moengagePlugin.initialise();
_moengagePlugin.identifyUser("flutter-uid"); //Pass any unique value for your user
```
**Multiple Identifiers**
If your application supports multiple login identifiers, such as an email ID, user ID, or mobile number, pass all relevant identifiers to the SDK using the following function:
```dart Dart wrap theme={null}
import 'package:moengage_flutter/moengage_flutter.dart';
final MoEngageFlutter _moengagePlugin = MoEngageFlutter();
_moengagePlugin.initialise();
_moengagePlugin.identifyUser({"email": "flutter@moengage.com", "id": "flutter"});
```
Updates are made to SDK functions to improve user identification and session management.
* **Forced Logout**: The MoEngage SDK no longer automatically logs out the previous user when a new user is detected on the device. Logout should now be explicitly called for workspaces enabled with Identity resolution to avoid data corruption.
* **SetUniqueID**: *IdentifyUser* function supports multiple identifiers, which replaces the need of using *SetUniqueID* function for user identification. Note that *SetUniqueID* is marked for removal in the future releases of SDK versions - it is important to use *identifyUser* instead especially if you are using Identity resolution in your workspace.
* **SetAlias**: For workspaces with the Identity resolution feature enabled, MoEngage SDK stores the previous identifier values. When *IdentifyUser* function is used to track the new identifier values, MoEngage SDK detects the change in identifier value and reports accordingly.
* If you call the *IdentifyUser* function without logging out, then the existing logged-in user's ID is updated.
If a network issue prevents the identifier from reaching the MoEngage server, the SDK caches the `identifyUser()` call and retries automatically once the network is available or the next time the app opens.
To enable or disable the SDK and data tracking, refer to [Compliance](/docs/developer-guide/flutter-sdk/compliance/compliance).
**Note**: Before implementing ***identifyUser()*** with multiple identifiers, you must activate the defined identifiers on the MoEngage dashboard. For configuration steps, see [this documentation](https://www.moengage.com/docs/user-guide/data/user-data/unified-identity-identity-resolution#configure-multiple-identifiers).
***Behaviour of Multiple identifyUser() calls***
* When calling ***identifyUser()*** multiple times, the new identifiers are appended to the existing list rather than replacing them. Here's an example of how this works:
```dart Dart wrap theme={null}
import 'package:moengage_flutter/moengage_flutter.dart';
final MoEngageFlutter _moengagePlugin = MoEngageFlutter();
_moengagePlugin.initialise();
//First call with email _moengagePlugin.identifyUser({"u_em": "flutter@moengage.com"});
//Later call identifyUser() with mobile Number
_moengagePlugin.identifyUser({"u_mb":"999999999"});
//Result now the user has both email and mobile identifiers;
```
* If you call ***identifyUser()*** with an identifier key that already exists, the new value will be update the existing one. Here's an example of how this works:
```dart Dart wrap theme={null}
import 'package:moengage_flutter/moengage_flutter.dart';
final MoEngageFlutter _moengagePlugin = MoEngageFlutter();
_moengagePlugin.initialise();
//First call with initial email _moengagePlugin.identifyUser({"u_em":"abc@xyz.com"});
//Later call identifyUser() when User updates their email
_moengagePlugin.identifyUser({"u_em":"jfk@xyz.com"});
//Result now the user email is updated with the later one;
```
This behaviour allows you to:
* Add new identifiers as they become available
* Update specific identifiers without affecting others
* Build a complete user identity profile over time
Here, `u_em`, `u_mb` are standard user attributes. Please refer to [this section](/docs/developer-guide/react-native-sdk/data-tracking/tracking-user-attributes-and-user-identity) to identify user with more standard user attributes
## Standard and Custom Attributes
**Standard attributes:** These are common user attributes that are pre-defined within the MoEngage dashboard, such as email address and mobile phone number. The table below lists these standard attributes and their corresponding key names
| User Attribute Name | Key name to be used in identifyUser method |
| ------------------------ | ------------------------------------------ |
| ID | uid |
| Email (Standard) | u\_em |
| Gender | u\_gd |
| Birthday | u\_bd |
| Name | u\_n |
| First Name | u\_fn |
| Last Name | u\_ln |
| Mobile Number (Standard) | u\_mb |
**Custom attributes:** These are attributes that you define yourself within the MoEngage dashboard in addition to the standard attributes. Here's an example of how you might work with custom attributes:
```Dart Dart wrap theme={null}
import 'package:moengage_flutter/moengage_flutter.dart';final MoEngageFlutter _moengagePlugin = MoEngageFlutter();_moengagePlugin.initialise();
//replace custom_attribute_name with the actual name of your custom user attribute and attributeValue with the actual value you want to assign to the attribute
_moengagePlugin.identifyUser({ custom_attribute_name: 'attributeValue' });
//you can set two or more identities at the same time
_moengagePlugin.identifyUser({ cust_attr_1: 'value1', cust_attr_2: 'value2' });
//you can set custom user identity and standard user identity at the same time
_moengagePlugin.identifyUser({ cust_attr_1: 'value1', cust_attr_2: 'value2', u_em: 'emailValue@emailDomain.com' });
```
For detailed instructions on selecting both custom and standard attributes when configuring multiple identifiers, please refer to [this document](https://www.moengage.com/docs/user-guide/data/user-data/unified-identity-identity-resolution#step-1-select-identifiers).
## Logout User
The application needs to notify the MoEngage SDK whenever the user is logged out of the application. To notify the SDK, call the API whenever the user is logged out of the application.
```dart Dart wrap theme={null}
import 'package:moengage_flutter/moengage_flutter.dart';
final MoEngageFlutter _moengagePlugin = MoEngageFlutter(YOUR_WORKSPACE_ID);
_moengagePlugin.initialise();
_moengagePlugin.logout();
```
**Critical - Very Important Integration Guideline**
Never use both the login methods - `identifyUser` and `setUserUniqueID()`(method to assign identifier present in versions below 9.2.0) in your project. Use only either one of the methods. Using both the methods can lead to inconsistent user profile creation and merging in your MoEngage account.
### Logout Callback Listener
To receive a callback when logout is complete, register a listener for the `onLogoutComplete` event:
```dart Dart wrap theme={null}
import 'package:moengage_flutter/moengage_flutter.dart';
final MoEngageFlutter _moengagePlugin = MoEngageFlutter(YOUR_WORKSPACE_ID);
void _onLogoutComplete(LogoutCompleteData data) {
// process your logout complete here
print("Logout completed: $data");
}
// Set this before initialise() so no callback is missed
_moengagePlugin.setLogoutCompleteCallbackHandler(_onLogoutComplete);
```
The logout callback listener requires Flutter SDK version 10.8.0 or later.
### Logout Callback Data
```dart Dart wrap theme={null}
class LogoutCompleteData {
Platforms platform; // Platforms.android | Platforms.iOS
AccountMeta accountMeta;
}
class AccountMeta {
String appId; // MoEngage Workspace ID for the instance
}
/// platform — platform the logout occurred on.
/// accountMeta — account info for the logged-out user; accountMeta.appId is the Workspace ID.
/// All types resolve from the single package:moengage_flutter/moengage_flutter.dart import.
```
# Tracking User Attributes
Use the following helper methods to set User attributes like Name, Email, Mobile, Gender, etc.
```dart Dart wrap theme={null}
import 'package:moengage_flutter/moengage_flutter.dart';
final MoEngageFlutter _moengagePlugin = MoEngageFlutter(YOUR_WORKSPACE_ID);
_moengagePlugin.initialise();
_moengagePlugin.setUserName("User Name");
_moengagePlugin.setFirstName("FirstName");
_moengagePlugin.setLastName("LastName");
_moengagePlugin.setEmail("EmailID");
_moengagePlugin.setPhoneNumber("PhoneNumber");
_moengagePlugin.setGender(MoEGender.male); // Supported values also include MoEGender.female OR MoEGender.other
_moengagePlugin.setLocation(new MoEGeoLocation(23.1, 21.2)); // Pass coordinates with MoEGeoLocation instance
_moengagePlugin.setBirthDate("2000-12-02T08:26:21.170Z"); // date format - ` yyyy-MM-dd'T'HH:mm:ss.fff'Z'`
```
For setting other User Attributes, you can use the generic method ***setUserAttribute(key,value)***
To set custom user attributes, you will have to provide the attribute name as shown below:
```dart Dart wrap theme={null}
import 'package:moengage_flutter/moengage_flutter.dart';
final MoEngageFlutter _moengagePlugin = MoEngageFlutter(YOUR_WORKSPACE_ID);
_moengagePlugin.initialise();
_moengagePlugin.setUserAttribute("int-attr", 0);
_moengagePlugin.setUserAttribute("bool-attr", true);
_moengagePlugin.setUserAttribute("string-attr", "Some Value");
_moengagePlugin.setUserAttribute("double-attr", 10.0);
_moengagePlugin.setUserAttribute("int-arr-attr", [100,200,300]);
_moengagePlugin.setUserAttribute("string-arr-attr", ["a","b","c"]);
_moengagePlugin.setUserAttribute("double-arr-attr", [1.0,2.0,3.0]);
_moengagePlugin.setUserAttribute('product', {'item-id' : 123,'item-type' : 'books','item-cost' : {'amount' : 100,'currency' : 'USD'}});
_moengagePlugin.setUserAttribute('products', [{'item-id' : 123,'item-cost' : {'amount' : 100,'currency' : 'USD'}},{'item-id' : 323,'item-cost' : {'amount' : 90,'currency' : 'USD'}}]);
```
You can not use "moe\_" as a prefix while naming events, event attributes, or user attributes. It is a system prefix and using it might result in periodic blacklisting without prior communication.
## Tracking Date as user attributes:
To track any date as user attributes use the ***setUserAttributeIsoDate(name, date)***. This API takes the attribute name and ISO Date as input.\
Date Format -***yyyy-MM-dd'T'HH:mm:ss.fff'Z***'\
Example:
```dart Dart wrap theme={null}
import 'package:moengage_flutter/moengage_flutter.dart';
final MoEngageFlutter _moengagePlugin = MoEngageFlutter(YOUR_WORKSPACE_ID);
_moengagePlugin.initialise();
_moengagePlugin.setUserAttributeIsoDate("timeStamp", "2019-12-02T08:26:21.170Z")
```
## Tracking Location as user attributes: (Not available for Web)
To track any location as user attributes use the *setUserAttributeLocation()*. This API takes the attribute name and an instance of MoEGeoLocation for coordinates as input.\
Example:
```dart Dart wrap theme={null}
import 'package:moengage_flutter/moengage_flutter.dart';
final MoEngageFlutter _moengagePlugin = MoEngageFlutter(YOUR_WORKSPACE_ID);
_moengagePlugin.initialise();
_moengagePlugin.setUserAttributeLocation("locationAttr", new MoEGeoLocation(72.8, 53.2));
```
## Custom Boolean User Attribute
### iOS (optional)
Starting from version 8.x.x of **moengage\_flutter**, the default tracking for the custom boolean attribute will be changed to ***boolean(true/false)*** from ***double(0/1)***. To configure this, use ***AnalyticsConfig*** with ***shouldTrackUserAttributeBooleanAsNumber*** and pass true to track the boolean as double. By default, this is set as ***false*** to track boolean as true/false.
Refer to the example below
```dart Dart wrap theme={null}
import 'package:moengage_flutter/moengage_flutter.dart';
final MoEngageFlutter _moengagePlugin = MoEngageFlutter(YOUR_WORKSPACE_ID, moEInitConfig: MoEInitConfig(analyticsConfig: AnalyticsConfig(shouldTrackUserAttributeBooleanAsNumber:true)));
@override
void initState() {
super.initState();
initPlatformState();
_moengagePlugin.initialise();
}
```
Refer to the example code below for tracking the boolean user attribute
```dart Dart wrap theme={null}
import 'package:moengage_flutter/moengage_flutter.dart';
final MoEngageFlutter _moengagePlugin = MoEngageFlutter(YOUR_WORKSPACE_ID, moEInitConfig: MoEInitConfig(analyticsConfig: AnalyticsConfig(shouldTrackUserAttributeBooleanAsNumber: false)));
_moengagePlugin.initialise();
// If shouldTrackUserAttributeBooleanAsNumber is passed as true then `bool-attr-false` will tracked with value 0 else false
_moengagePlugin.setUserAttribute("bool-attr-false", false);
// If shouldTrackUserAttributeBooleanAsNumber is passed as true then `bool-attr-true` will tracked with value 1 else true
_moengagePlugin.setUserAttribute("bool-attr-true", true);
```
## Reserved keywords for User Attributes
Below is the list of keys that should not be used when tracking user attributes.
* USER\_ATTRIBUTE\_UNIQUE\_ID
* USER\_ATTRIBUTE\_USER\_EMAIL
* USER\_ATTRIBUTE\_USER\_MOBILE
* USER\_ATTRIBUTE\_USER\_NAME
* USER\_ATTRIBUTE\_USER\_GENDER
* USER\_ATTRIBUTE\_USER\_FIRST\_NAME
* USER\_ATTRIBUTE\_USER\_LAST\_NAME
* USER\_ATTRIBUTE\_USER\_BDAY
* USER\_ATTRIBUTE\_NOTIFICATION\_PREF
* USER\_ATTRIBUTE\_OLD\_ID
* MOE\_TIME\_FORMAT
* MOE\_TIME\_TIMEZONE
* USER\_ATTRIBUTE\_DND\_START\_TIME
* USER\_ATTRIBUTE\_DND\_END\_TIME
* MOE\_GAID
* MOE\_ISLAT
* INSTALL
* UPDATE
* status
* user\_id
* source
You can not use "moe\_" as a prefix while naming events, event attributes, or user attributes. It is a system prefix and using it might result in periodic blacklisting without prior communication.
# Flutter SDK Deprecated Versions
Source: https://moengage.com/docs/developer-guide/flutter-sdk/deprecated-versions/deprecated-versions
Check the support status of MoEngage Flutter SDK major versions: which are current, which are still supported, and which are deprecated, along with deprecation dates.
Use this page to check the support status of MoEngage Flutter SDK major versions. It lists which versions are current, which are still supported, and which are deprecated, along with deprecation dates. This is the source of truth for Flutter SDK version status. For how the lifecycle works and what deprecation means for your app, see the [SDK Deprecation Policy](/docs/developer-guide/sdk-lifecycle-and-policies/sdk-deprecation-policy). To set up the Flutter SDK, see the [integration guide](/docs/developer-guide/flutter-sdk/overview/getting-started-with-flutter-sdk).
## Version Support Status
**Current** — The latest major version. It receives new features, fixes, and support.
**Supported** — An older major version within its 3-year support window. It continues to receive support.
**Deprecated** — A version outside its support window. It no longer receives fixes or support.
| Major Version | Status | Deprecation Date | Notes |
| -------------------- | ---------- | ---------------- | -------------------------------------------------------------------------------------------------------------- |
| Core 10.x | Current | TBD | Latest major version. Receives new features, fixes, and support. |
| Core 9.x | Supported | TBD | Receives support. |
| Core 8.x | Supported | TBD | Receives support. |
| Core 7.2.0 and below | Deprecated | August 2027 | Support, debugging, and fixes are no longer available. Data continues to flow. Upgrade to the current version. |
MoEngage supports each major version for 3 years from its initial release; deprecation then takes effect the following August. See the [SDK Deprecation Policy — Support Window](/docs/developer-guide/sdk-lifecycle-and-policies/sdk-deprecation-policy#support-window) for details. This table is updated when MoEngage announces further deprecations. To receive advance notice, subscribe to the SDK newsletter and ensure your workspace administrator email is current.
## Understanding Deprecated Status
A deprecated version continues to send and receive data. MoEngage does not block traffic. After the deprecation date, MoEngage no longer debugs, fixes, or supports issues specific to that version, and the resolution for any issue is to upgrade to the current version. All fixes ship in the current version only and are not available on supported or deprecated versions. For the complete policy, see the [SDK Deprecation Policy](/docs/developer-guide/sdk-lifecycle-and-policies/sdk-deprecation-policy).
## Upgrading to a Supported Version
* Refer to the [Flutter SDK release notes](/docs/release-notes/sdks/flutter) for the current major version changes.
* Review the [Flutter-SDK](https://github.com/moengage/Flutter-SDK/) repository for the latest packages.
* For help, contact your MoEngage Customer Success Manager (CSM) or the Support team.
## Frequently Asked Questions
Apps on deprecated versions continue to run and data continues to flow. However, MoEngage no longer provides support, debugging, or fixes for deprecated versions. Upgrading to the current or a supported version using the [Flutter SDK release notes](/docs/release-notes/sdks/flutter) ensures you receive continued support.
MoEngage sends advance notice 6 to 12 months before the deprecation date through email to workspace administrators, the quarterly SDK newsletter, in-dashboard notices, and SDK release notes. See the [SDK Deprecation Policy](/docs/developer-guide/sdk-lifecycle-and-policies/sdk-deprecation-policy) for the full notification schedule.
# InApp NATIV
Source: https://moengage.com/docs/developer-guide/flutter-sdk/in-app-messages/inapp-nativ
Display in-app messages in your Flutter app using the MoEngage SDK with platform-specific configuration.
In-App Messaging is custom views that you can send to a segment of users to show custom messages or give new offers or take to some specific pages. They can be created from your MoEngage account.
## Installing Android Dependency
### **Requirements for displaying images and GIFs in InApp**
Starting InApp version **7.0.0,** SDK requires [Glide](https://bumptech.github.io/glide/) to show images and GIFs in the in-apps. You need to add the below dependency in your **build.gradle** file.
```groovy Groovy theme={null}
dependencies {
...
implementation("com.github.bumptech.glide:glide:4.16.0")
}
```
Additional dependency installation is not required for iOS.
# Show InApp
MoEngage refreshes the list of eligible In-App campaigns via a meta API call. During an active session, the list refreshes when the app returns from the background to the foreground (at most once every 15 minutes), and immediately when a new session starts or the app is killed and relaunched.
Call the below API to show an inApp campaign on a screen. You will have to handle the redirection of the user when they click on the inApps unless it's a rich landing navigation.
```Dart Dart theme={null}
import 'package:moengage_flutter/moengage_flutter.dart';
final MoEngageFlutter _moengagePlugin = MoEngageFlutter(YOUR_WORKSPACE_ID);
_moengagePlugin.initialise();
_moengagePlugin.showInApp();
```
# Display Nudges
Starting with ***moengage\_flutter*** ***version 7.0.0*** MoEngage InApp SDK supports displaying Non-Intrusive nudges. This API is supported only in Android & IOS. You will have to handle the redirection of the user when they click on the inApps unless it's a rich landing navigation.
To show a Nudge InApp Campaign call `showNudge()`
```javascript Dart theme={null}
import 'package:moengage_flutter/moengage_flutter.dart';
final MoEngageFlutter _moengagePlugin = MoEngageFlutter(YOUR_WORKSPACE_ID);
_moengagePlugin.showNudge(); // Display Nudge on any available position
_moengagePlugin.showNudge(position: MoEngageNudgePosition.top); // Display Nudge on the specific position
```
# InApp/Nudge Redirection default behavior
On clicking an Inapp or Nudge, MoEngage SDKs will handle **only rich landing navigation** redirection.
For the screen name and deep link redirection, your app code should redirect the user to the right screen or deep link. To handle the screen name and deep link redirection, you must implement inapp click callback methods. MoEngage SDK will just pass the inapp payload to this call back code. Implementation steps are mentioned in the InApp callback section of the Integration.
# Self Handled InApps
Self-handled In Apps are messages that are delivered by the SDK to the application, and the application builds the UI using the delivered payload by the SDK. To get the self-handled in-app, use the below API.
```Dart Dart theme={null}
import 'package:moengage_flutter/moengage_flutter.dart';
final MoEngageFlutter _moengagePlugin = MoEngageFlutter(YOUR_WORKSPACE_ID);
_moengagePlugin.getSelfHandledInApp();
```
To get the self-handled campaign, register for the callback as shown below.
## Multiple Self-Handled InApps
* This feature requires a minimum moengage\_flutter version **9.0.0**
* Event-triggered multiple self-handled inapps are not supported.
Fetch Multiple Self Handled Campaigns using [*getSelfHandledInApps()*](https://pub.dev/documentation/moengage_flutter/latest/moengage_flutter/MoEngageFlutter/getSelfHandledInApps.html). The MoEngage SDK will return up to 5 campaigns(in the order of campaign priority set at the time of campaign creation). The function will return self-handled in-app data in the future.
```Dart Dart theme={null}
import 'package:moengage_flutter/moengage_flutter.dart';
MoEngageFlutter _moengagePlugin = MoEngageFlutter(YOUR_WORKSPACE_ID);
_moengagePlugin.getSelfHandledInApps().then((campaignsData) {
// Show the Self Handled InApps
}).catchError((e) {
// Error occurred while fetching the campaigns
});
```
It will return data of type [SelfHandledInAppsData](https://pub.dev/documentation/moengage_flutter/latest/moengage_flutter/SelfHandledCampaignsData-class.html).
### Tracking Statistics for Multiple Self-Handled In-Apps
The *onCampaignsAvailable()* callback method returns [*SelfHandledCampaignsData*](https://moengage.github.io/android-api-reference/inapp/com.moengage.inapp.model/-self-handled-campaigns-data/index.html)\*\*,\*\*which contains a list of [*SelfHandledCampaignData*](https://moengage.github.io/android-api-reference/inapp/com.moengage.inapp.model/-self-handled-campaign-data/index.html) objects. The statistics for each *SelfHandledCampaignData* object must be tracked individually below APIs.
### Fetching Contextual Multiple Self-Handled InApps
To fetch contextual multiple self-handled inapps, set the inapp contexts using [*setCurrentContext()*](https://pub.dev/documentation/moengage_flutter/latest/moengage_flutter/MoEngageFlutter/setCurrentContext.html) before calling \*[getSelfHandledInApps()](https://pub.dev/documentation/moengage_flutter/latest/moengage_flutter/MoEngageFlutter/getSelfHandledInApps.html).\*This will return a list of contextual and non-contextual inapps(in the order of campaign priority set at the time of campaign creation).
## Tracking Statistics for Self-Handled In-Apps
The application must notify MoEngage SDK whenever the In-App messages are displayed, clicked on, or dismissed, as the application controls these actions. The following methods are called to notify the SDK. The data object provided to the application in the callback for self-handled in-app should be passed as a parameter to the following APIs.
```Dart Dart theme={null}
import 'package:moengage_flutter/moengage_flutter.dart';
final MoEngageFlutter _moengagePlugin = MoEngageFlutter(YOUR_WORKSPACE_ID);
// call this method to notify campaign was shown.
_moengagePlugin.selfHandledShown(message);
// call this method to noftify campaign was clicked.
_moengagePlugin.selfHandledClicked(message);
// call this method to notify campaign was dismissed.
_moengagePlugin.selfHandledDismissed(message);
```
# InApp Callbacks
We provide callbacks for in-app shown, in-app clicked, in-app dismissed, and self-Handled in-app available events. You can register for the same as shown below.
The callbacks must be registered before inapps are displayed either via show methods or triggered events. Make sure you are calling `initialise()` the method of the plugin after you set up these callbacks. Refer [doc](/docs/developer-guide/react-native-sdk/sdk-integration/react-native/sdk-initialization/manual-initialization/framework-initialization) for more info.
```Dart Dart theme={null}
import 'package:moengage_flutter/moengage_flutter.dart';
void _onInAppClick(ClickData message) {
print("This is a inapp click callback from native to flutter. Payload " +
message.toString());
}
void _onInAppShown(InAppData message) {
print("This is a callback on inapp shown from native to flutter. Payload " +
message.toString());
}
void _onInAppDismiss(InAppData message) {
print("This is a callback on inapp dismiss from native to flutter. Payload " +
message.toString());
}
void _onInAppSelfHandle(SelfHandledCampaignData? message) {
if (message == null) {
debugPrint('$tag _onInAppSelfHandle(): SelfHandled InApp Data is Null');
return;
}
print("This is a callback on inapp self handle from native to flutter. Payload " +
message.toString());
}
final MoEngageFlutter _moengagePlugin = MoEngageFlutter(YOUR_WORKSPACE_ID);
//Register for callbacks
_moengagePlugin.setInAppClickHandler(_onInAppClick);
_moengagePlugin.setInAppShownCallbackHandler(_onInAppShown);
_moengagePlugin.setInAppDismissedCallbackHandler(_onInAppDismiss);
_moengagePlugin.setSelfHandledInAppHandler(_onInAppSelfHandle);
//NOTE: set up callbacks before initialise()
_moengagePlugin.initialise();
```
# Contextual InApp
You can restrict the in-apps based on the user's context in the application apart from restricting InApp campaigns on a specific screen. To set the user's context in the application use *setCurrentContext()* API as shown below.
## Set Context
Call the below method to set the context in the *initState()* method of the widget before calling *showInApp().*
```Dart Dart theme={null}
import 'package:moengage_flutter/moengage_flutter.dart';
final MoEngageFlutter _moengagePlugin = MoEngageFlutter(YOUR_WORKSPACE_ID);
_moengagePlugin.setCurrentContext(['C1', 'C2']);
```
## Reset Context
Once the user is moving out of the context use the *resetCurrentContext()* API to reset/clear the existing context.
```Dart Dart theme={null}
import 'package:moengage_flutter/moengage_flutter.dart';
final MoEngageFlutter _moengagePlugin = MoEngageFlutter(YOUR_WORKSPACE_ID);
_moengagePlugin.resetCurrentContext();
```
For more information on Contextual InApp, refer to the video tutorial available in [Troubleshooting and FAQs](/docs/developer-guide/android-sdk/troubleshooting-and-faqs/troubleshooting-and-faqs).
# InApp Payload
InApp Data will be received in the below format:
```Dart Dart theme={null}
class InAppData {
/// Native platform from which the callback was triggered.
Platforms platform;
//Account Data
AccountMeta accountMeta;
///In-App Campaign Data
CampaignData campaignData;
}
class CampaignData {
/// Unique identifier for each campaign.
String campaignId;
///Campaign Name
String campaignName;
...
}
class ClickData {
/// Native platform from which the callback was triggered.
Platforms platform;
/// Account Data
AccountMeta accountMeta;
/// In-App Campaign Data
CampaignData campaignData;
/// Action data with type navigation/custom
Action action;
}
class NavigationAction extends Action {
/// Type of Navigation action.
/// Possible value deep_linking or screen
NavigationType navigationType;
/// Deeplink Url or the Screen Name used for the action.
String navigationUrl;
/// [Map] of Key-Value pairs entered on the MoEngage Platform for
/// navigation action of the campaign.
Map keyValuePairs;
}
class CustomAction extends Action {
///Key-Value Pair entered on the MoEngage Platform during campaign creation.
Map keyValuePairs;
}
class SelfHandledCampaignData {
/// Native platform from which the callback was triggered.
Platforms platform;
/// Account Data
AccountMeta accountMeta;
/// In-App Campaign Data
CampaignData campaignData;
//Self handled data
SelfHandledCampaign campaign;
}
class SelfHandledCampaign {
/// Self handled campaign payload.
String payload;
/// Interval after which in-app should be dismissed, unit - Seconds
int dismissInterval;
/// InApp Campaign Display Rules
Rules displayRules;
}
class Rules {
/// Screen name on which the campaign should be shown.
@Deprecated('This field is deprecated and will be removed in future releases. Use [screenNames] instead')
String? screenName;
/// Context for which the campaign should be shown.
List context;
/// Screen Names on which the campaign can be shown.
/// @since 10.0.0
List screenNames;
}
```
# Handling Orientation Change
This is only for the Android platform
Starting SDK version `4.1.0` in-apps are supported in both portrait and landscape modes.\
SDK has to be notified when the device orientation changes for SDK to handle in-app display.
There are two ways to do it:
1. Add the API call in the Android native part of your app
2. Call MoEngage plugin's `onOrientationChanged()`
## Add the API call in the Android native part of your app
Notify the SDK when **onConfigurationChanged()** API callback is received in your App's Activity class.
```Dart Dart theme={null}
public class MainActivity extends FlutterActivity {
...
@Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
MoEFlutterHelper.getInstance().onConfigurationChanged();
...
}
...
}
```
## Call MoEngage plugin's orientation change API
Call the below API to notify SDK of the orientation change.
```Dart Dart theme={null}
_moengagePlugin.onOrientationChanged();
```
# Getting Started with Flutter SDK
Source: https://moengage.com/docs/developer-guide/flutter-sdk/overview/getting-started-with-flutter-sdk
Get started with the MoEngage Flutter SDK for push notifications, in-app messages, and event tracking.
# Overview
MoEngage’s Flutter SDK helps you integrate MoEngage into iOS and Android applications built with Flutter. It allows you to work with push notifications, in-app messages, cards, user attributes, events, and much more.
To see the sample code, take a look at the [GitHub repository](/docs/developer-guide/flutter-sdk/sample-app/flutter-sample-app). This article describes the steps to implement MoEngage features on Flutter.
You can now get notified whenever MoEngage releases a new version of the Flutter SDK. For more information, refer to [Subscribe to MoEngage SDK Releases](/docs/release-notes/sdks/flutter).
Review the [SDK Deprecation Policy](/docs/developer-guide/sdk-lifecycle-and-policies/sdk-deprecation-policy) for supported SDK versions and deprecation timelines before integrating.
# SDK Installation and Initialization
**Step 1: Installation**
To add MoEngage's Flutter SDK to your application, refer to [Installation Methods](/docs/developer-guide/flutter-sdk/sdk-integration/sdk-installation/framework-dependency).
**Step 2: Complete Native Setup**\
The platform-specific native setup guidelines to complete the installation are described in the following articles:
* [Android Setup](/docs/developer-guide/flutter-sdk/sdk-integration/sdk-installation/android)
* [iOS Setup](/docs/developer-guide/flutter-sdk/sdk-integration/sdk-installation/ios)
* [Web Setup](/docs/developer-guide/flutter-sdk/sdk-integration/sdk-installation/framework-dependency)
**Step 3: Framework Initialization**\
Initialize an instance of the MoEngage plugin by calling the [Framework Initialization](/docs/developer-guide/flutter-sdk/sdk-integration/sdk-initialization/manual-initialization/framework-initialization) method.
**Step 4: Platform Initialization**\
The platform-specific steps to initialize the SDK and set up the data center are described in the following articles:
* [Android](/docs/developer-guide/flutter-sdk/sdk-integration/sdk-initialization/manual-initialization/android-sdk-initialization)
* [iOS](/docs/developer-guide/flutter-sdk/sdk-integration/sdk-initialization/manual-initialization/ios-sdk-initialization)
* [Web](/docs/developer-guide/flutter-sdk/sdk-integration/sdk-initialization/web-sdk-initialization)
# Data Tracking
Data tracking allows apps to monitor and analyze user behavior to optimize engagement strategies. It involves tracking various user actions such as login, logout, and event tracking in a way that avoids data corruption. Use the following methods to implement data tracking.
* **Install/Update Differentiation -** To track fresh installs and updates separately, refer to the methods in this [article](/docs/developer-guide/flutter-sdk/data-tracking/install-update-differentiation).
* **Tracking Login, Logout, and Setting Unique ID** - To avoid data corruption, it is crucial to follow the steps outlined in the following articles when handling user login and logout.
* [Tracking Login and set user ID](/docs/developer-guide/flutter-sdk/data-tracking/setting-unique-id-for-sdk-versions-below-920)
* [Tracking Logout](/docs/developer-guide/flutter-sdk/data-tracking/setting-unique-id-for-sdk-versions-below-920)
* [Updating User Attribute Unique ID](/docs/developer-guide/flutter-sdk/data-tracking/setting-unique-id-for-sdk-versions-below-920)\
\
It is essential to have a unique ID for each of your app's users, which can be passed onto MoEngage SDK using setUniqueId(). This unique ID helps to correctly identify a user across multiple installs and platforms to provide a unified view.
Once a user logs out of the app, it's critical to call logout() to initiate the creation of a new user. This step is necessary to ensure that any subsequent activities performed by the new user are not wrongly attributed to the previously logged-in user, which could distort user data.
* **Tracking user attributes** - To set custom attributes available in the user profile, refer to the methods in [this article](/docs/developer-guide/flutter-sdk/data-tracking/tracking-user-attributes-and-user-identity).
* **Tracking Events** - Tracking events is how you record user actions, along with any properties that describe the action. To track custom events, refer to the methods in [this article](/docs/developer-guide/flutter-sdk/data-tracking/tracking-events).
* **Enable Advertising Identifier Tracking (Android only)** - MoEngage SDK uses a Device ID (persistent device identifier) to uniquely identify the user to deliver personalized content and associates this to AAID if allowed by the app. This allows accurate identification of reachable devices for sending push notifications and tracking re-installs for users over time. To enable tracking of the AAID after obtaining the user’s consent, refer to the methods in [this article](/docs/developer-guide/android-sdk/data-tracking/basic/enable-advertising-identifier-tracking).
# Push Notifications
Push campaigns target users through notifications for your app or website. Depending on the desired capability, follow the integration steps listed below to integrate push notifications.
## Basic Setup - Android
Follow the basic setup outlined in this section to enable push notifications on an Android device using MoEngage.
* **FCM Setup on MoEngage Dashboard -** FCM Authentication is the method to enable sending push notifications to your app installed on Android devices. Use the methods mentioned in [this article](/docs/developer-guide/android-sdk/push/basic/fcm-authentication) to authenticate MoEngage to access Firebase services.
* **Adding metadata for push notification -** Set the small icon and large icon drawable and other options to handle push notifications using the methods available in [this article](/docs/developer-guide/android-sdk/push/basic/push-configuration).
* **Android Notification Runtime Permissions** - When the application is running on Android 13 to show notifications to the user, applications would need to request the user for notification permission. Refer to the methods available in [this article](/docs/developer-guide/flutter-sdk/push/basic/android-notification-runtime-permissions) to handle permission requests.
* **Push Registration and Receiving** - To use Push Notification in your Flutter application, you need to configure Firebase. Depending on your requirements, refer to one of the below methods to enable Push Registration and Receiving.
**Add messaging service**\
You must add the messaging service to the Manifest file for MoEngage SDK to show the notifications. Refer to this document [here](/docs/developer-guide/android-sdk/push/basic/push-token-registration-and-display).
**Callback on token registration(optional)**\
To get an optional callback whenever a new token is registered or the token is refreshed, refer to the method [here](/docs/developer-guide/android-sdk/push/basic/push-token-registration-and-display).
**Notification Clicked Callback**
To receive a callback whenever a push is clicked and for custom handling redirection, use the method in [this article](/docs/developer-guide/android-sdk/push/advanced/callbacks-and-customisation).
**How to opt out of MoEngage Registration?**\
The MoEngage SDK attempts to register for a push token; since your application is handling push, you need to opt out of SDK's token registration using the method in [this article](/docs/developer-guide/android-sdk/push/basic/push-token-registration-and-display).
**Pass the Push Token To MoEngage SDK** - After receiving the push token from FCM, use the method in [this article](/docs/developer-guide/android-sdk/push/basic/push-token-registration-and-display) to pass the Push Token to the MoEngage SDK to set it in the MoEngage platform.
**Passing the Push payload to the MoEngage SDK** - After receiving the push payload on the app, use the method in [this article](/docs/developer-guide/android-sdk/push/basic/push-token-registration-and-display) to send out push notifications to the device.
We recommend you use the Android native APIs to pass the push payload to the MoEngage SDK instead of the Flutter/Dart APIs. Flutter Engine might not get initialized if the application is in the killed state, which will lead to poor push reachability or delivery.
* [Pass the Push Token To MoEngage SDK](/docs/developer-guide/flutter-sdk/push/basic/android-push-configuration#passing-push-token)
* [Pass the Push payload to the MoEngage SDK](/docs/developer-guide/flutter-sdk/push/basic/android-push-configuration#basic-setup)
* [Callbacks and customizations](/docs/developer-guide/flutter-sdk/push/basic/android-push-configuration#customizing-push-notification)
**Notification Clicked Callback -** MoEngage's Flutter plugin optionally provides a callback on push clicks with the method in [this article](/docs/developer-guide/flutter-sdk/push/basic/push-callback).
## Basic Setup - iOS
Follow the basic setup outlined in this section to enable push notifications on an iOS device using MoEngage.
* **APNS Setup on MoEngage dashboard**\
APNS Authentication is the method to enable sending push notifications to your app installed on Android devices. You can use any of these options to set up APNS on the MoEngage dashboard.
* [APNS Authentication Key (recommended)](/docs/developer-guide/ios-sdk/push/basic/apns-authentication-key)
* [APNS Certificate/PEM file](/docs/developer-guide/ios-sdk/push/basic/apns-certificate-pem-file-legacy)
* **App target implementation** - Make changes to your app target to enable notifications by following the steps mentioned in [this article](/docs/developer-guide/ios-sdk/push/basic/ios-push-integration-tutorial).
* **Provide the App Group ID to SDK**- Pass the App Group ID to MoEngage SDK using the method in [this article](/docs/developer-guide/ios-sdk/push/basic/ios-push-integration-tutorial).
* **Push Registration and Receiving**
In MoEngage SDK, we have now swizzled the AppDelegate Class to get all the callbacks related to Push Notifications, and we have also applied the method swizzling for UserNotificationCenter delegate methods. This is to ease the integration of the SDK.
**Registering for Push notification**\
Follow the steps in [this article](/docs/developer-guide/ios-sdk/push/basic/ios-push-integration-tutorial) to initiate registration.
**Callback methods on receiving Push Notification**\
The callback the app would receive on receiving the push notifications is mentioned in [this article](/docs/developer-guide/ios-sdk/push/basic/ios-push-integration-tutorial). With Swizzling enabled, no additional configuration is required.
In case you do not prefer to use swizzling, you can disable the same by adding the flag MoEngageAppDelegateProxyEnabled in the app’s Info.plist file and setting it to Boolean value NO, and follow the steps below.
**Registering for Push notification -** Follow the steps in [this article](/docs/developer-guide/ios-sdk/push/basic/ios-push-integration-tutorial) to initiate registration and the steps in [this section](/docs/developer-guide/ios-sdk/push/basic/ios-push-integration-tutorial) to call the respective MoEngage SDK methods for registration callbacks.
**Callback methods on receiving Push Notification -** With Swizzling disabled, include calls to MoEngage SDK methods on receiving notification callbacks, as described in [this article](/docs/developer-guide/ios-sdk/push/basic/ios-push-integration-tutorial).
* **Disable Badge Reset**\
By default, the SDK sets the notification badge count to 0 on every app launch, and this also clears the notifications in the device notification center. If you would like to keep the notifications even after the App Launch, then disable badge reset by calling the method in [this section](/docs/developer-guide/ios-sdk/push/advanced/custom-notification-handling).
* **Custom Sound for Notification**\
To optionally set a custom tone for notifications of your app, you can follow the method in [this section](/docs/developer-guide/ios-sdk/push/advanced/custom-notification-handling).
* **Notification Service Extension Target Implementation**\
The notification service extension allows MoEngage SDK to customize the content of a notification before the system delivers it to the user. This gives you the capability to add media in notifications, support inbox, update badge count on notifications delivered, and track notification impressions. Follow the steps in [this article](/docs/developer-guide/ios-sdk/push/basic/ios-push-integration-tutorial) to set up the notification service extension.
* **Notification Actions**\
Actionable notifications let you add custom action buttons to the standard iOS push notifications. Follow the steps mentioned in [this article](/docs/developer-guide/ios-sdk/push/basic/actionable-notifications) to add custom actions to your notifications and track the actions performed on notifications
This completes your basic setup for push notifications in Flutter.
## Push Templates
Push templates enable you to craft beautiful notifications within minutes without any coding. For information on how to create campaigns with templates in the dashboard, refer to [this article](/docs/developer-guide/ios-sdk/push/optional/push-templates).
To enable push templates, please follow the platform-specific documentation
* [Android](/docs/developer-guide/android-sdk/push/optional/push-templates)
* [iOS](/docs/developer-guide/ios-sdk/push/optional/push-templates)
## Push Amp+
A significant percentage of notifications, around 25-30%, is not delivered due to issues with original equipment manufacturer (OEM) devices. To combat this problem and improve retention rates, MoEngage developed Push Amplification+ to reach customers who may not have received notifications. MoEngage has also partnered with OEMs to address these issues and ensure that notifications are reliably delivered. To minimize any additional burden on your application, we have developed individual software development kits (SDKs) for each OEM. You can choose and integrate the relevant SDK based on your application's specific needs and device share. Refer to the documentation for each OEM-specific service and integrate the appropriate ones into your application for optimal push notification delivery.
### Supported Integrations
* [HMS Push Kit](/docs/developer-guide/android-sdk/push/optional/push-amp-plus/configuring-hms-push-kit)
## Push Amplification
Push Amplification works as a fallback mechanism when Firebase Cloud Messaging (FCM) fails to deliver Push Notifications. Follow the method here to set up [Push Amplification](/docs/developer-guide/android-sdk/push/optional/push-amplification).
## Notification Center
The Notification Center shows your push notification history, allowing you to provide an option for the end-user to scroll back and see what they have missed. MoEngage provides out-of-box inbox support with a fully customizable default UI and also provides an option to build your own Notification Center. For more information, refer to [Notification Center](/docs/developer-guide/flutter-sdk/push/optional/notification-center).
## Location Triggered Notifications
Location-triggered notifications allow you to send messages to your audience that are triggered on the user’s entry, exit, and dwell in defined Geo Fences. Follow the method in [this article](/docs/developer-guide/flutter-sdk/push/optional/location-triggered) to set up location triggers.
## Device triggered notifications
Device-triggered notifications allow you to send messages to your audience that are triggered locally based on any activity on a device. Offline delivery of messages is supported as well.
To enable device-triggered notifications, use the following platform-specific articles:
* [Android](/docs/developer-guide/android-sdk/push/optional/device-triggered)
* [iOS](/docs/developer-guide/ios-sdk/push/optional/real-time-triggers)
## Advanced Use Cases in Android
For advanced use cases, the following options are available:
* **Non-MoEngage Payload** - To get an optional callback in case a push payload is received for any other server apart from the MoEngage Platform, refer to the method [here](/docs/developer-guide/android-sdk/push/basic/push-token-registration-and-display).
* **Callbacks and customizations** - The MoEngage SDK allows the client application to optionally customize the notification display and extend/customize the behavior of the notification. Refer to the methods mentioned [here](/docs/developer-guide/android-sdk/push/advanced/callbacks-and-customisation) to access the features, such as:
* Control whether a notification is shown to the user or not
* Notification Received Callback
* Notification Clicked Callback
* Notification Cleared Callback
* Custom Action on Action Button Click
* **Push Display Handled by Application(Android)** - When the application needs to handle the push display on the client side, you can track notification impressions and cases using the methods described in [this article](/docs/developer-guide/android-sdk/push/advanced/push-display-handled-by-application).
# In-App
MoEngage In-App Campaigns target users by showing a message while the user is using your app. They are effective in providing contextual information and help to cross-sell/up-sell on desired screens of your app or/and on desired actions performed by the user in your app.
Basic Setup - To install In-app notifications in Flutter, use the following platform-specific methods:
* [Android](/docs/developer-guide/flutter-sdk/in-app-messages/inapp-nativ)
* iOS (installation is not required for iOS)
## Displaying In-App Messages
You can either show In-app messages using MoEngage’s out-of-the-box UI, or you can use Self-handled In-apps to build the UI of the application using the payload from MoEngage.
**Show In-app**\
Call the method [here](/docs/developer-guide/flutter-sdk/in-app-messages/inapp-nativ) to show an inApp campaign on a screen. In-app pop-ups will only show up where showInApp() method is called.
**Handling Orientation Change**\
In-apps are supported in both portrait and landscape modes. There are two ways to do it:
* [Add the API call in the Android native part of your app](/docs/developer-guide/flutter-sdk/in-app-messages/inapp-nativ)
* [Call MoEngage plugin's onOrientationChanged()](/docs/developer-guide/flutter-sdk/in-app-messages/inapp-nativ)
**Self-handled In-apps**\
Self-handled In Apps are messages that are delivered by the SDK to the application, and the application builds the UI using the delivered payload by the SDK. To get the self-handled in-app, refer to [this article](/docs/developer-guide/flutter-sdk/in-app-messages/inapp-nativ).
* [Getting self-handled campaigns](/docs/developer-guide/flutter-sdk/in-app-messages/inapp-nativ)
* [Tracking Statistics for Self-Handled In-Apps](/docs/developer-guide/android-sdk/in-app-messages/in-app-nativ#tracking-statistics-for-self-handled-in-apps)
## InApp Callbacks
Optionally, we provide callbacks for in-app shown, in-app clicked, in-app dismissed, and self-handled in-app available events. You can register for the callbacks using the methods in [this article.](/docs/developer-guide/flutter-sdk/in-app-messages/inapp-nativ#inapp-callbacks)
# Cards
MoEngage Cards campaigns help you interact with your users with persistent and non-intrusive messages in your customer's journey. Self-handled cards are message payloads that are delivered by the SDK to the application, and the application builds the UI using the delivered payload.
Refer to [this article](/docs/developer-guide/flutter-sdk/cards/self-handled-cards) to implement self-handled cards on Flutter.
Cards are currently not supported for web platform.
# Personalize Data Payload
Source: https://moengage.com/docs/developer-guide/flutter-sdk/personalize/personalize-data-payload
Review the data models and payload structure returned by the MoEngage Flutter Personalize SDK.
Review the data models and payload structure returned by the MoEngage Flutter Personalize SDK.
## DataSource
```dart Dart wrap theme={null}
enum DataSource {
/** Returned from local cache. */
cache,
/** Fetched from the MoEngage backend. */
network,
}
```
## ExperienceStatus
```dart Dart theme={null}
enum ExperienceStatus {
/** Currently running. */
active,
/** Manually paused on the dashboard. */
paused,
/** Scheduled to start in the future. */
scheduled,
}
```
## ExperienceFailureReason
```dart Dart theme={null}
enum ExperienceFailureReason {
userInCampaignControlGroup, // USER_IN_CAMPAIGN_CONTROL_GROUP
userInGlobalControlGroup, // USER_IN_GLOBAL_CONTROL_GROUP
userNotInSegment, // USER_NOT_IN_SEGMENT
invalidExperienceKey, // INVALID_EXPERIENCE_KEY
maxLimitBreached, // MAX_LIMIT_BREACHED
experienceNotActive, // EXPERIENCE_NOT_ACTIVE
experienceExpired, // EXPERIENCE_EXPIRED
personalizationFailed, // PERSONALIZATION_FAILED
}
```
## ExperienceCampaign
```dart Dart theme={null}
class ExperienceCampaign {
/// The unique identifier for the experience.
String experienceKey;
/// The JSON payload containing personalization data.
Map payload;
/// Context for tracking (passed to impression/click events).
Map experienceContext;
/// Whether data came from cache or network.
DataSource source;
}
```
## ExperienceCampaignFailure
```dart Dart theme={null}
class ExperienceCampaignFailure {
/// The failure reason.
ExperienceFailureReason reason;
/// Experience keys affected by this failure.
List experienceKeys;
}
```
## ExperienceCampaignsResult
```dart Dart theme={null}
class ExperienceCampaignsResult {
/// Successfully fetched experience campaigns.
List experiences;
/// Per-key failures (business logic errors from server).
List failures;
}
```
## ExperienceCampaignMeta
```dart Dart theme={null}
class ExperienceCampaignMeta {
/// The unique identifier for the experience.
String experienceKey;
/// The display name of the experience.
String experienceName;
/// The current status of the experience.
ExperienceStatus status;
}
```
## ExperienceCampaignsMetadata
```dart Dart theme={null}
class ExperienceCampaignsMetadata {
/// Whether data came from cache or network.
DataSource source;
/// List of experience metadata entries.
List experiences;
}
```
## PersonalizeError
```dart Dart theme={null}
class PersonalizeError implements Exception {
String code; // e.g. 'SDK_NOT_INITIALIZED', 'NETWORK_ERROR'
String message;
}
```
# Personalize SDK
Source: https://moengage.com/docs/developer-guide/flutter-sdk/personalize/personalize-sdk
Learn how to integrate the MoEngage Personalize SDK for Flutter to fetch personalized content, handle offering campaigns, and track performance.
# Overview
The MoEngage Personalize SDK provides a secure framework for delivering personalized campaigns. It simplifies integration by handling user identity and authentication internally, eliminating the need to manage API secrets or manual HTTPS calls.
**Prerequisite**
Before you can fetch personalized experiences, ensure the core MoEngage SDK has been initialized in your application. For more information, refer to Flutter SDK Initialization.
# How It All Fits Together
Before writing any code, it is helpful to understand the three moving parts of the personalization workflow:
1. **Dashboard Configuration:** A marketer creates an Experience Campaign in the MoEngage dashboard and assigns it a unique `experienceKey` (e.g., `home_banner`). They configure the specific JSON payload to be returned for different user segments.
2. **The Meta Call :** Your application calls `fetchExperiencesMeta` to discover which experience keys are active and available for the current user.
3. **The Fetch Call :** Your application calls `fetchExperience` or `fetchExperiences` with a specific key to retrieve the actual payload. The SDK uses the metadata gathered in Step 2 to accurately resolve and return this request.
The SDK returns **raw** JSON only. As the developer, you are responsible for parsing this payload and building the corresponding UI in your application.
# Integrating MoEngage Personalization
To add MoEngage's Personalize SDK to your project, edit the application's **pubspec.yaml** file and use the command below.
```yaml pubspec.yaml wrap theme={null}
dependencies:
moengage_personalize: $latestVersion
```
***\$latestVersion*** refers to the latest version of the plugin.
Post including the dependency, run ***flutter pub get*** command in the terminal to install the dependency.
This plugin is dependent on **moengage\_flutter** plugin. Make sure you have installed the **moengage\_flutter** plugin as well. Refer to the [documentation](/docs/developer-guide/flutter-sdk/sdk-integration/sdk-installation/framework-dependency) for the same.
## Initialize Personalize
After installing the plugin, initialize the MoEngage Personalize module using the following configuration.
```dart Dart theme={null}
import 'package:moengage_personalize/moengage_personalize.dart';
final personalize = MoEngagePersonalize('YOUR_WORKSPACE_ID');
```
# Implementation Workflow
The Personalize helper for Flutter is designed to simplify the retrieval and interaction with dynamic, personalized content. Below is a breakdown of the workflow and code placeholders.
## 1. Fetch Meta Experience
Before fetching any specific payload or experience, you must invoke the metadata call. Prefetching the metadata helps you optimize the experience fetch. On the app side, you can use the metadata to identify the right personalized content for the current UI state and fetch only the relevant content instead of all the content in the application.
```dart Dart theme={null}
import 'package:moengage_personalize/moengage_personalize.dart';
final personalize = MoEngagePersonalize('YOUR_WORKSPACE_ID');
// Create a list with the desired experience statuses
final statuses = [ExperienceStatus.active];
personalize.fetchExperiencesMeta(statuses)
.then((metadata) => print(metadata)) // add logic here to process the metadata
.catchError((e) => print(e)); // add logic for fallback/error handling
```
The returned `Future` resolves with an [`ExperienceCampaignsMetadata`](/docs/developer-guide/flutter-sdk/personalize/personalize-data-payload) object containing all necessary metadata for campaign execution. The `catchError` handler receives a [`PersonalizeError`](/docs/developer-guide/flutter-sdk/personalize/personalize-data-payload) with a `code` and `message` identifying the reason for failure.
## 2. Fetch Personalized Content
Once metadata is fetched, you can retrieve the actual personalized payloads. You can fetch a single experience or multiple experiences simultaneously, with full support for contextual targeting.
EXPERIENCE\_KEY is the unique key that is used in the experience campaign while creating the campaign on the MoEngage dashboard. You can find this key in the `ExperienceCampaignMeta` object of the `ExperienceCampaignsMetadata` returned on successful metadata fetch.
```dart Dart wrap theme={null}
import 'package:moengage_personalize/moengage_personalize.dart';
final personalize = MoEngagePersonalize('YOUR_WORKSPACE_ID');
// additional attributes you want to pass for the experience.
final attributes = {};
```
### Fetch Single Experience
* **Single Experiences**: Retrieve a single experience using a single experience key.
```dart Dart wrap theme={null}
personalize.fetchExperience('', attributes: attributes)
.then((result) => print('Single Experience: $result'))
.catchError((e) => print(e));
```
### Fetch Multiple Experience
* **Bulk Experiences**: Retrieve multiple experiences by passing an array of experience keys.
```dart Dart wrap theme={null}
personalize.fetchExperiences(experienceKeys, attributes: attributes)
.then((result) => print('Multiple Experiences: $result'))
.catchError((e) => print(e));
```
The returned `Future` resolves with an `ExperienceCampaignsResult` object. On success, the result contains two fields:
* `experiences` — successfully resolved ExperienceCampaign objects
* `failures` — [`ExperienceCampaignFailure`](/docs/developer-guide/flutter-sdk/personalize/personalize-data-payload) objects for keys that could not be resolved, each with a reason and `experienceKeys`.
Throws [`PersonalizeError`](/docs/developer-guide/flutter-sdk/personalize/personalize-data-payload) (with code and message) for system-level failures such as network errors or SDK not initialised."
Use Cases:
**Contextual Targeting**: Pass an object of attributes (e.g., `{"current_page": "home", "cart_value": "500"}`) during the fetch. This enables real-time, state-dependent content delivery (e.g., showing a "Free Shipping" banner if the cart value meets a threshold).
To accurately measure campaign performance, you must track user impressions after rendering the personalized content on the UI.
## 3. Notify SDK on Showing the Experience (Track Impressions)
An *impression* is a telemetry event that notifies the MoEngage SDK that a personalized campaign payload has successfully rendered on the UI and is visible to the user.
To accurately track campaign performance, you must invoke the following methods the moment your application displays the personalized content on the screen.
### 3a. Notify SDK for Experience Campaigns
Call the `experiencesShown()` method when the UI element containing the experience renders.
```dart Dart wrap theme={null}
import 'package:moengage_personalize/moengage_personalize.dart';
final personalize = MoEngagePersonalize('YOUR_WORKSPACE_ID');
// campaigns is a list of ExperienceCampaign objects received from fetchExperiences
// Pass the exact objects or payloads previously received from the fetchExperiences() method.
final List campaigns = [/* campaign objects */];
personalize.experiencesShown(campaigns);
```
### 3b. Track Impressions for Offering Campaigns
[Offerings](/docs/user-guide/decisioning/offer-decisioning/create-offerings) are a distinct subtype of personalized content configured by marketers on the MoEngage dashboard (such as dynamic product recommendations, catalogs, or unique coupon codes). Before tracking, it is important to understand how to handle offering payloads within your application:
* Fetching an Offering: You retrieve an offering payload by calling the `fetchExperience()` or `fetchExperiences()` methods. The SDK processes the metadata and returns the payload within the `ExperienceCampaignsResult` object.
* Identifying an Offering: You can identify an offering by inspecting the structure of the returned JSON. An offering payload consists of structured data nested under a specific custom offering key.
* Building the Offering UI: The SDK only returns this offering data as raw JSON. As the developer, you must write the logic to parse this JSON payload and build the corresponding visual UI components on the screen.
Once the offering UI is built and successfully rendered to the user, the SDK provides dedicated tracking functions that accept offering-specific attributes.
```dart Dart wrap theme={null}
import 'package:moengage_personalize/moengage_personalize.dart';
final personalize = MoEngagePersonalize('YOUR_WORKSPACE_ID');
// List of offering payload dicts for a specific offering campaign
final List
## 4. Track Clicks
### 4a. Track Clicks for Experience Campaigns
Use these methods to log "Clicks" when the user interacts with the UI element.
```dart Dart wrap theme={null}
import 'package:moengage_personalize/moengage_personalize.dart';
final personalize = MoEngagePersonalize('YOUR_WORKSPACE_ID');
// campaign is an array of ExperienceCampaign objects
// Pass the exact objects or payloads previously received from the fetchExperiences() method.
final ExperienceCampaign campaign = /* campaign object */;
personalize.experienceClicked(campaign);
```
### 4b. Track Clicks for Offering Campaigns
[Offerings](/docs/user-guide/decisioning/offer-decisioning/create-offerings) are a distinct subtype of personalized content configured by marketers on the MoEngage dashboard (such as dynamic product recommendations, catalogs, or unique coupon codes). Before tracking, it is important to understand how to handle offering payloads within your application:
* Fetching an Offering: You retrieve an offering payload by calling the `fetchExperience()` or `fetchExperiences()` methods. The SDK processes the metadata and returns the payload within the `ExperienceCampaignsResult` object.
* Identifying an Offering: You can identify an offering by inspecting the structure of the returned JSON. An offering payload consists of structured data nested under a specific custom offering key.
* Building the Offering UI: The SDK only returns this offering data as raw JSON. As the developer, you must write the logic to parse this JSON payload and build the corresponding visual UI components on the screen.
For interactions with specific items (such as a product recommendation or a coupon) contained within an offering campaign:
```dart Dart wrap theme={null}
import 'package:moengage_personalize/moengage_personalize.dart';
final personalize = MoEngagePersonalize('YOUR_WORKSPACE_ID');
// campaign is a single ExperienceCampaign object
// Pass the exact objects or payloads previously received from the fetchExperiences() method.
final ExperienceCampaign campaign = /* campaign object */;
// Map containing offering details for a specific offering campaign
final Map offeringPayload = {/* offering payload */};
personalize.offeringClicked(campaign, offeringPayload);
```
#### Example
```dart Sample Payload theme={null}
{
"custom_offering_key": {
// Expected offering payload in the function call.
}
}
```
You can use these Offering-specific functions only if the data is part of an offering payload. For all other experience data, use the standard experience shown/clicked functions.
# FAQs
You can fetch up to 25 experiences in a single call. If you exceed this, the SDK returns the 25 most recently updated experiences and notifies you of the unfulfilled keys.
The SDK returns an empty payload along with a standardized error code (e.g., NETWORK\_ERROR).
# Android Notification Runtime Permissions
Source: https://moengage.com/docs/developer-guide/flutter-sdk/push/basic/android-notification-runtime-permissions
Handle Android 13 notification runtime permissions in your Flutter app using the MoEngage SDK.
Android 13 (API level 33) and higher supports [runtime permission](https://developer.android.com/guide/topics/permissions/overview#runtime) for sending [non-exempt](https://developer.android.com/develop/ui/views/notifications/notification-permission#exemptions)(including Foreground Services (FGS)) notifications from an app [POST\_NOTIFICATIONS](https://developer.android.com/reference/android/Manifest.permission#POST_NOTIFICATIONS). This change helps users focus on the notifications that are most important to them.
Refer to the [official documentation](https://developer.android.com/develop/ui/views/notifications/notification-permission) for more details.
The below-mentioned APIs are supported starting MoEngage core Android SDK version **12.3.01**
When an application runs on Android 13 and wants to show notifications to the user, it must request the user's notification permission. You have two options: let MoEngage handle permissions for you or handle the notification permission with your code.
* MoEngage handles Notification permission.
* You just have to call a single line of code mentioned on this page.
* You maintain the notification permission logic.
* Notify MoEngage SDK if permission to push notifications is granted.
We recommend you let MoEngage handle push notification permissions.
## Let MoEngage SDK handle Notification permission
MoEngage SDK provides helper APIs to show the permission request to the end-user or navigate the user to the settings screen for enabling notifications.
Use the below API to show the permission request dialog to the user.
When using the below API SDK automatically tracks the response, sets up the required notification channel, etc, mentioned above.
```dart Dart theme={null}
import 'package:moengage_flutter/moengage_flutter.dart';
final MoEngageFlutter _moengagePlugin = MoEngageFlutter(YOUR_WORKSPACE_ID);
_moengagePlugin.initialise();
_moengagePlugin.requestPushPermissionAndroid();
```
## Notify SDK of permission result
Once the application requests the user for notification permission notify the SDK of the user response using the below API. *If you are letting MoEngage SDK handle the notification permissions, you should ignore this step.*
```dart Dart theme={null}
import 'package:moengage_flutter/moengage_flutter.dart';
final MoEngageFlutter _moengagePlugin = MoEngageFlutter(YOUR_WORKSPACE_ID);
_moengagePlugin.initialise();
_moengagePlugin.pushPermissionResponseAndroid(isGranted);
```
## Navigate to Notification Settings (situational/optional)
Use the below API to show navigate the user to the Notification Settings for the application on the device.
*Note*: Below Android 8(API level 26) the user is directed to the Application Info screen of your application.
```dart Dart theme={null}
import 'package:moengage_flutter/moengage_flutter.dart';
final MoEngageFlutter _moengagePlugin = MoEngageFlutter(YOUR_WORKSPACE_ID);
_moengagePlugin.initialise();
_moengagePlugin.navigateToSettingsAndroid();
```
The update push permission count API is supported starting version **12.6.00.**
## Update the Permission request count
Once the application requests the user for notification permission, update the SDK of the request attempts.
### Why does the SDK require permission attempt count?
SDK requires the attempt count to accurately track the number of times the permission request was attempted. If the user denies the permission request twice the application/SDK cannot request permission further i.e. if we request permission it would be automatically denied without the user seeing the request dialog. As an SDK we cannot be sure of the number of times the user has denied the permission we try to optimize the calls to request permission based on the attempt count.
```Dart Dart theme={null}
import 'package:moengage_flutter/moengage_flutter.dart';
final MoEngageFlutter _moengagePlugin = MoEngageFlutter(YOUR_WORKSPACE_ID);
_moengagePlugin.initialise();
_moengagePlugin.updatePushPermissionRequestCountAndroid(requestCount);
```
# Android Push Configuration
Source: https://moengage.com/docs/developer-guide/flutter-sdk/push/basic/android-push-configuration
Configure push notifications for Android in your Flutter app including FCM setup and permissions.
# Basic setup
Follow the basic setup outlined in this section to enable push notifications on an Android device using MoEngage.
* **FCM Setup on MoEngage Dashboard -** FCM Authentication is the method to enable sending push notifications to your app installed on Android devices. Use the methods mentioned in [this article](/docs/developer-guide/android-sdk/push/basic/fcm-authentication) to authenticate MoEngage to access Firebase services.
* **Adding metadata for push notification -** Set the small icon and large icon drawable and other options to handle push notifications using the methods available in [this article](/docs/developer-guide/android-sdk/push/basic/push-configuration).
* **Android Notification Runtime Permissions** - When the application is running on Android 13 to show notifications to the user, applications would need to request the user for notification permission. Refer to the methods available in [this article](/docs/developer-guide/flutter-sdk/push/basic/android-notification-runtime-permissions) to handle permission requests.
* **Push Registration and Receiving** - To use Push Notification in your Flutter application, you need to configure Firebase. Depending on your requirements, refer to one of the below methods to enable Push Registration and Receiving.
**Add messaging service**
Starting from version [10.4.0](https://github.com/moengage/Flutter-SDK/releases/tag/moengage_flutter-v10.4.0), the SDK automatically adds the Firebase service declaration to `AndroidManifest.xml`. The file `MoEFireBaseMessagingService` is declared with low priority (android:priority="-1") in `AndroidManifest.xml`. file to prevent conflicts.
| Integration Scenario | System Behavior | Action Required |
| ----------------------- | --------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| MoEngage Only | The SDK automatically handles push payloads and token updates using the built-in service. | Update & Build: Ensure you are on the latest SDK version and run `flutter clean` before building. No manifest changes are needed. |
| Multiple Push Providers | The Android system prioritizes your messaging service over the `MoEFireBaseMessagingService`. | Pass Data Manually: In your custom `FirebaseMessagingService`, intercept the payload and token. [Pass them to the MoEngage SDK](/docs/developer-guide/android-sdk/push/basic/push-token-registration-and-display) APIs if the data originates from the MoEngage platform. |
For versions below 10.4.0, You must add the `MoEFireBaseMessagingService` to the `AndroidManifest.xml` file for MoEngage SDK to show the notifications. Refer [here](/docs/developer-guide/android-sdk/push/basic/push-token-registration-and-display).
**Callback on token registration(optional)**\
To get an optional callback whenever a new token is registered or the token is refreshed, refer to the method [here](/docs/developer-guide/android-sdk/push/basic/push-token-registration-and-display#token-callback-access-to-push-token-optional).
**Notification Clicked Callback**\
To receive a callback whenever a push is clicked and for custom handling redirection, use the method in [this article](/docs/developer-guide/android-sdk/push/advanced/callbacks-and-customisation#notification-clicked-callback).
**How to opt out of MoEngage Registration?**\
The MoEngage SDK attempts to register for a push token; since your application handles push, you need to opt out of SDK's token registration using the method in [this article](/docs/developer-guide/android-sdk/push/basic/push-token-registration-and-display#how-to-opt-out-of-moengage-push-token-registration).
**Pass the Push Token To MoEngage SDK** - After receiving the push token from FCM, use the method in [this article](/docs/developer-guide/android-sdk/push/basic/push-token-registration-and-display#how-to-opt-out-of-moengage-push-token-registration) to pass the Push Token to the MoEngage SDK to set it in the MoEngage platform.
**Passing the Push payload to the MoEngage SDK** - After receiving the push payload on the app, use the method in [this article](/docs/developer-guide/android-sdk/push/basic/push-token-registration-and-display#passing-the-push-payload-to-the-moengage-sdk) to send out push notifications to the device.
We recommend you use the Android native APIs to pass the push payload to the MoEngage SDK instead of the Flutter/Dart APIs. Flutter Engine might not get initialized if the application is in the killed state, which will lead to poor push reachability or delivery.
* [Pass the Push Token To MoEngage SDK](/docs/developer-guide/flutter-sdk/push/basic/android-push-configuration#passing-push-token)
* [Pass the Push payload to the MoEngage SDK](/docs/developer-guide/flutter-sdk/push/basic/android-push-configuration#basic-setup)
* [Callbacks and customizations](/docs/developer-guide/flutter-sdk/push/basic/android-push-configuration#customizing-push-notification)
**Notification Clicked Callback -** MoEngage's Flutter plugin optionally provides a callback on push clicks with the method in [this article](/docs/developer-guide/flutter-sdk/push/basic/push-callback).
# Flutter APIs for Push
You can skip this section completely if you let MoEngage handle push token registration and display or use Android Native methods to pass tokens and payload to MoEngage SDKs.
Read on if you want to use Flutter APIs of MoEngage SDK to pass tokens and payload to MoEngage SDKs.
We recommend you use the Android native APIs to pass the push payload to the MoEngage SDK instead of the Flutter/Dart APIs. Flutter Engine might not get initialized if the application is in the killed state, which will lead to poor push reachability or delivery.
## Passing Push Token
```auto Dart theme={null}
import 'package:moengage_flutter/moengage_flutter.dart';
final MoEngageFlutter _moengagePlugin = MoEngageFlutter(YOUR_WORKSPACE_ID);
_moengagePlugin.initialise();
_moengagePlugin.passFCMPushToken();
```
## Customizing Push notification
If required the application can customize the behavior of notifications by using Native Android code (Java/Kotlin). To learn more about the customization refer to the [Advanced Push Configuration](/docs/developer-guide/android-sdk/push/advanced/callbacks-and-customisation) documentation.Instead of extending ***PushMessageListener*** as mentioned in the above document extend ***PluginPushCallback.***
Refer to the below documentation for Push Amp+, Push Templates, and Geofence.
* [Push Amplification](/docs/developer-guide/android-sdk/push/optional/push-amplification)
* [Push Amp Plus](/docs/developer-guide/android-sdk/push/optional/push-amp-plus/push-amp-plus-integration)
* [Push Templates](/docs/developer-guide/android-sdk/push/optional/push-templates)
* [GeoFence Push](/docs/developer-guide/android-sdk/push/optional/location-triggered)
# Migrate To The Extension Integrator Tool
Source: https://moengage.com/docs/developer-guide/flutter-sdk/push/basic/integrating-moengage-service-and-content-extension-in-ios/migrating-to-the-extension-integrator-tool
Migrate from a manual iOS notification extension setup to the MoEngage Extension Integrator Tool in your Flutter app.
All steps in this guide are performed in Xcode, open your iOS project by launching `ios/Runner.xcworkspace` in Xcode before proceeding.
To migrate from an existing manual implementation to the integrator tool, follow the below steps:
### Step 1: Prerequisites
Before proceeding, ensure the following are in place:
* Ensure you are using MoEngage Flutter SDK version [10.6.0](/docs/release-notes/sdks/flutter#core-10-6-0) or above to utilize the extension integrator tool.
* **SDK initialization:** Initialize the MoEngage SDK using [file-based initialization](/docs/developer-guide/flutter-sdk/sdk-integration/sdk-initialization/file-based-initialization).
* **App Group configuration:** Provide the `AppGroupName` key (for example, `group.com.organization.app`) you are using with your `sdkConfig.appGroupID`.
* After integration is complete, you may be prompted to access the keychain for code signing. Click **Always Allow.**
### Step 2: Integrate MoEngageRichNotification
Integrate `MoEngageRichNotification` if you need to support either of the following push notification features:
* **Rich media** — display images, GIFs, or video in the notification banner
* **Rich push templates** — render interactive notification layouts such as carousel
This step is mandatory if you have integrated the content extension.
To install the `MoEngageRichNotification` through SPM, perform the following steps:
1. Navigate to **File > Add Package**.
2. Enter the repository URL:
* `https://github.com/moengage/apple-sdk.git`
3. Select the **master** branch or a specific version and select **Add Package**.
4. Target the package to your application.
**Information** CocoaPods is being deprecated. MoEngage recommends using Swift Package Manager for all new integrations. For more info, refer [here](/docs/developer-guide/ios-sdk/sdk-integration/basic/sdk-integration).
1. Add the following line to your `Podfile` inside your app target.
The `moengage_flutter` package already brings in `MoEngage-iOS-SDK`, you only need to add the `RichNotification` subspec.
```ruby lines wrap theme={null}
pod 'MoEngage-iOS-SDK/RichNotification'
```
2. Run pod install:
```ruby lines wrap theme={null}
pod repo update
pod install
```
### Step 3: Integrate the extension integrator tool
Automate the extension configuration by adding a custom script to your build process.
1. In Xcode, select your application target, go to **Build Phases**, and click **+** to add a **New Run Script Phase**.
If you have already added a script phase and configured the input files, you can simply update the run command as described in step 3 below.
2. Add the following paths to the **Input Files** section:
* `$(BUILT_PRODUCTS_DIR)/$(INFOPLIST_PATH)`
* `$(INSTALL_DIR)/$(INFOPLIST_PATH)`
3. In the shell script input box of the Run Script Phase added in step 1, enter the command relevant to your dependency manager, replacing `$OPTIONS` with your desired configuration:
```bash Swift Package Manager (SPM) lines wrap theme={null}
${OBJROOT}/../../SourcePackages/artifacts/apple-sdk/moengage-extensions-integration/moengage-extensions-integration.artifactbundle/moengage-extensions-integration/bin/moengage-extensions-integration $OPTIONS
```
```bash CocoaPods lines wrap theme={null}
${PODS_ROOT}/MoEngageExtensionsIntegration/moengage-extensions-integration.artifactbundle/moengage-extensions-integration/bin/moengage-extensions-integration $OPTIONS
```
#### Available options
Replace `$OPTIONS` with one or more of the following:
| Option | Description |
| :--------------------------------------------------------------------- | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `--enable-push-notification-templates` | Required if the content extension from Step 2 is used. |
| `--notification-service-extension-name $CUSTOM_SERVICE_EXTENSION_NAME` | Sets a custom name for the service extension. Use this when migrating to this tool from a custom service extension implementation. (Default: `MoEngageNotificationService`). |
| `--notification-content-extension-name $CUSTOM_CONTENT_EXTENSION_NAME` | Sets a custom name for the content extension. Use this when migrating to this tool from a custom content extension implementation. (Default: `MoEngageNotificationContent`). |
### Step 3: Remove existing extensions
Remove existing service and content extensions added in **Frameworks**, **Libraries** and **Embedded Content** section.
Failing to remove prevents push delivery impressions and rich push notifications.
# Set Up iOS Notification Extensions for Rich Push and Impression Tracking
Source: https://moengage.com/docs/developer-guide/flutter-sdk/push/basic/integrating-moengage-service-and-content-extension-in-ios/set-up-ios-notification-extensions-for-rich-push-and-impression-tracking
Required configuration to enable push impression tracking and rich media notifications on iOS. Skipping this results in missing impression metrics and rich push failures.
All steps in this guide are performed in Xcode, open your iOS project by launching `ios/Runner.xcworkspace` in Xcode before proceeding.
This guide describes how to integrate MoEngage service and content extensions into your iOS application. These extensions enable notification impression tracking, support for rich media (images, GIFs, and video), and the use of rich push notification templates.
## Integrating service and content extensions
Without these extensions, MoEngage cannot track push impressions, [rich media](https://www.moengage.com/docs/user-guide/campaigns-and-channels/mobile-push/notification-features-and-behavior/gifs-in-push-notifications) notifications fall back to plain text, and [rich push templates](https://www.moengage.com/docs/user-guide/campaigns-and-channels/mobile-push/create/push-templates) render with fallback template.
| Use Case | Service Extension (Step 2) | Content Extension (Step 3) | MoEngageRichNotification (Step 4) | Integrator Tool Flag |
| :--------------------------------------- | :------------------------- | :------------------------- | :-------------------------------- | :------------------------------------- |
| Push impression tracking | ✅ Required | — | — | (default) |
| Rich media in push (images, GIFs, video) | ✅ Required | — | ✅ Required | (default) |
| MoEngage rich push templates (carousel) | ✅ Required | ✅ Required | ✅ Required | `--enable-push-notification-templates` |
### Step 1: Prerequisites
Before proceeding, ensure the following are in place:
* Ensure you are using MoEngage Flutter SDK version [10.6.0](/docs/release-notes/sdks/flutter#core-10-6-0) or above to utilize the extension integrator tool.
* **SDK initialization:** Initialize the MoEngage SDK using [file-based initialization](/docs/developer-guide/flutter-sdk/sdk-integration/sdk-initialization/file-based-initialization).
* **App Group configuration:** Define an App Group in your configuration using the `AppGroupName` key (for example, `group.com.organization.app`).
* Add this App Group to the **Signing & Capabilities** in Xcode.
* After integration is complete, you may be prompted to access the keychain for code signing. Click **Always Allow.**
### Step 2: Configure the service extension
The service extension tracks notification impressions and downloads rich media content.
1. [Create an App Identifier](https://developer.apple.com/help/account/identifiers/register-an-app-id): Use the format `[appBundleId].[serviceExtensionName].`
1. Replace `[appBundleId]` with your application’s specific bundle identifier
2. Replace `[serviceExtensionName]` with your chosen name (which defaults to *MoEngageNotificationService*). For example *:* If your app bundle identifier is `com.org.app` and your extension is named `NotificationService`, the identifier is `com.org.app.NotificationService`.
If you already have an existing Notification Service Extension, you can reuse it — you do not need to create a new one. Pass its name using the `--notification-service-extension-name` flag when configuring the [Run Script Phase in Step 5](#step-5-integrate-the-extension-integrator-tool). The tool will inject the necessary MoEngage logic without overwriting your custom code.
2. Select the service extension identifier created from step-1, open the **Capabilities** tab, add [**App Groups**](https://developer.apple.com/help/account/identifiers/register-an-app-group). Enter the name matching your `AppGroupName` key.
3. **Generate provisioning profile:** On the [Apple Developer Portal](https://developer.apple.com/account/resources/profiles/list), [create a new provisioning profile](https://developer.apple.com/help/account/provisioning-profiles/create-an-app-store-provisioning-profile) for the identifier created in step 1, and download it. To download it in Xcode, click **Xcode** in the menu bar, choose **Settings**.
4. Switch to the **Apple Accounts** section, and select your Apple developer account. On your Apple Accounts page, select **Download Manual Profiles**.
If you don't need rich push templates, you can skip Steps 3 and 4 and move directly to [Step 5: Integrate the extension integrator tool](#step-5-integrate-the-extension-integrator-tool).
### Step 3: Configure the content extension (Optional)
The content extension is required only if you intend to use MoEngage rich push notification templates.
1. [Create an App Identifier](https://developer.apple.com/help/account/identifiers/register-an-app-id): Use the format `[appBundleId].[contentExtensionName].`
1. Replace `[appBundleId]` with your application’s specific bundle identifier
2. Replace `[contentExtensionName]` with your chosen name (which defaults to *MoEngageNotificationContent*). For example\_:\_ If your app bundle identifier is `com.org.app` and your extension is named `NotificationContent`, the identifier is `com.org.app.NotificationContent`.
2. Select the content extension identifier, open the **Capabilities** tab, add [**App Groups**](https://developer.apple.com/help/account/identifiers/register-an-app-group). Enter the name matching your `AppGroupName` key.
3. **Generate provisioning profile:** On the [Apple Developer Portal](https://developer.apple.com/account/resources/profiles/list), [create a new provisioning profile](https://developer.apple.com/help/account/provisioning-profiles/create-an-app-store-provisioning-profile) for the identifier created in step 1, and download it. To download it in Xcode, click **Xcode** in the menu bar, choose **Settings**.
4. Switch to the **Apple Accounts** section, and select your Apple developer account. On your Apple Accounts page, select **Download Manual Profiles**.
### Step 4: Integrate MoEngageRichNotification (Optional)
Integrate `MoEngageRichNotification` if you need to support either of the following push notification features:
* **Rich media** — display images, GIFs, or video in the notification banner
* **Rich push templates** — render interactive notification layouts such as carousel
This step is mandatory if you have integrated the content extension mentioned in Step 3.
To install the `MoEngageRichNotification` through SPM, perform the following steps:
1. Navigate to **File > Add Package**.
2. Enter the repository URL:
* `https://github.com/moengage/apple-sdk.git`
3. Select the **master** branch or a specific version and select **Add Package**.
4. Target the package to your application.
**Information** CocoaPods is being deprecated. MoEngage recommends using Swift Package Manager for all new integrations. For more info, refer [here](/docs/developer-guide/ios-sdk/sdk-integration/basic/sdk-integration).
1. Add the following line to your `Podfile` inside your app target.
The `moengage_flutter` package already brings in `MoEngage-iOS-SDK` via autolinking — you only need to add the `RichNotification` subspec.
```ruby lines wrap theme={null}
pod 'MoEngage-iOS-SDK/RichNotification'
```
2. Run pod install:
```ruby lines wrap theme={null}
pod repo update
pod install
```
### Step 5: Integrate the extension integrator tool
Automate the extension configuration by adding a custom script to your build process.
1. In Xcode, select your application target, go to **Build Phases**, and click **+** to add a **New Run Script Phase**.
2. Add the following paths to the **Input Files** section:
* `$(BUILT_PRODUCTS_DIR)/$(INFOPLIST_PATH)`
* `$(INSTALL_DIR)/$(INFOPLIST_PATH)`
3. Enter the command relevant to your dependency manager, replacing **integrate\_extensions** in above image.
If you have already added a script phase and configured the input files, you can simply update the run command as described below.
```bash Swift Package Manager (SPM) lines wrap theme={null}
${OBJROOT}/../../SourcePackages/artifacts/apple-sdk/moengage-extensions-integration/moengage-extensions-integration.artifactbundle/moengage-extensions-integration/bin/moengage-extensions-integration $OPTIONS
```
```bash CocoaPods lines wrap theme={null}
${PODS_ROOT}/MoEngageExtensionsIntegration/moengage-extensions-integration.artifactbundle/moengage-extensions-integration/bin/moengage-extensions-integration $OPTIONS
```
#### Available options
Replace `$OPTIONS` with one or more of the following:
| Option | Description |
| :--------------------------------------------------------------------- | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `--enable-push-notification-templates` | Required if the content extension from Step 3 is used. |
| `--notification-service-extension-name $CUSTOM_SERVICE_EXTENSION_NAME` | Sets a custom name for the service extension. Use this when migrating to this tool from a custom service extension implementation. (Default: `MoEngageNotificationService`). |
| `--notification-content-extension-name $CUSTOM_CONTENT_EXTENSION_NAME` | Sets a custom name for the content extension. Use this when migrating to this tool from a custom content extension implementation. (Default: `MoEngageNotificationContent`). |
### Example Command (SPM)
For integrating a service extension named `NotificationService` and a content extension named `NotificationContent`, the complete command for SPM would be:
```bash Swift Package Manager (SPM) lines wrap theme={null}
${OBJROOT}/../../SourcePackages/artifacts/apple-sdk/moengage-extensions-integration/moengage-extensions-integration.artifactbundle/moengage-extensions-integration/bin/moengage-extensions-integration --enable-push-notification-templates --notification-service-extension-name NotificationService --notification-content-extension-name NotificationContent
```
4. **Disable sandboxing:** In your application target **Build Settings**, set **USER\_SCRIPT\_SANDBOXING** to **No**.
## Migrating to the extension integrator tool
To migrate from an existing manual implementation to the integrator tool, refer [here](/docs/developer-guide/flutter-sdk/push/basic/integrating-moengage-service-and-content-extension-in-ios/migrating-to-the-extension-integrator-tool).
## Troubleshooting and FAQs
Review the build logs for detailed information.
Common causes include:
* **Incorrect Integration:** The setup does not follow the [Integrating Service & Content Extension](/docs/developer-guide/ios-sdk/push/basic/ios-push-integration-tutorial) guide.
* **Missing Configuration:** The `Info.plist` is missing required MoEngage configuration options.
* **App Group Mismatch:** The `AppGroupName` provided in `Info.plist` is not included in the service and content extension bundle identifier capabilities or the application entitlements.
* **Environment Mismatch:** The provisioning profile was not created for the specific build environment (e.g., using a Development profile when generating an App Store build).
* **Custom Directory Issues:** Provisioning profiles are not stored in default Xcode directories. Use the [additional configuration build settings](#troubleshooting-and-faqs) to pass custom paths.
* **Certificate Issues:** Missing or incorrect certificate configuration during the generation of the provisioning profile.
* **Expired Profiles:** The provisioning profile has expired. This requires re-generating and re-downloading the profile.
No, The Service Extension is mandatory for tracking notification impressions and downloading rich media (images/GIFs/video). The Content Extension is only required if you intend to use MoEngage's interactive *Rich Push Templates* (e.g., carousels or custom button layouts).
iOS extensions run in a separate sandbox from your main application. The **App Group** creates a shared container that allows the MoEngage SDK in the main app to share authentication tokens, user data, and local storage with the extension. Without it, the extension cannot verify the user or track impressions correctly.
The **Extension Integrator Tool** needs to access the project’s built products and provisioning profiles located in system folders outside the standard Xcode sandbox. If `ENABLE_USER_SCRIPT_SANDBOXING` is set to **Yes**, the script will be blocked, resulting in a "Permission Denied" error during the build phase.
Additional build settings can be used to provide specific configuration inputs to the integrator tool for both local and CI builds.
* **`MOENGAGE_EXTENSION_PROFILES_SEARCH_PATHS`:** Use this to provide additional folders to scan for your provisioning profiles. By default, the tool scans standard Xcode directories:
* `~/Library/Developer/Xcode/UserData/Provisioning Profiles`
* `~/Library/MobileDevice/Provisioning Profiles` If your provisioning profiles are not present in these folders, add additional paths with this build setting.
* **`MOENGAGE_NOTIFICATION_SERVICE_EXTENSION_PROFILE`:** Provide the explicit provisioning profile filename for the Notification Service Extension. The tool will use this filename instead of searching for a provisioning profile. The profile must be present in one of the folders above. Use this option if the tool is not able to pick the right provisioning profile.
* **`MOENGAGE_NOTIFICATION_CONTENT_EXTENSION_PROFILE`:** Provide the explicit provisioning profile filename for the Notification Content Extension. The tool will use this filename instead of searching for a provisioning profile. The profile must be present in one of the folders above. Use this option if the tool is not able to pick the right provisioning profile.
This usually stems from one of three technical gaps:
1. **App Group Mismatch:** Ensure the `AppGroupName` string in your `Info.plist` matches the Entitlements file exactly.
2. **Payload Timeout:** iOS gives extensions \~30 seconds to download media. If your assets are too large or the network is slow, it will fail over to plain text.
Yes, but be careful with the SPM path in Step 5. The path `${OBJROOT}/../../SourcePackages/...` assumes a standard Xcode structure. If your CI environment (like Jenkins or Bitrise) uses a custom build directory, you may need to provide the absolute path to the `moengage-extensions-integration` binary.
# iOS Push Configuration
Source: https://moengage.com/docs/developer-guide/flutter-sdk/push/basic/ios-push-configuration
Configure iOS push notifications in your Flutter app using APNs authentication keys or certificates.
# Configuring Push in iOS
Following are the two ways to configure Push Notification
## APNS Authentication Key :
To send push notifications to iOS users, it is required to generate the APNs Auth Key file for your application and upload it to the MoEngage dashboard. Refer to the [link](/docs/developer-guide/ios-sdk/push/basic/apns-authentication-key) to generate Auth key.
## APNS Certificate
First, you must create an APNS certificate and upload it to the dashboard to send push notifications on iOS. Follow the steps below to do that:
* [Create an APNS certificate](/docs/developer-guide/ios-sdk/push/basic/apns-certificate-pem-file-legacy)
* [Convert the resultant certificate to .pem format](/docs/developer-guide/ios-sdk/push/basic/apns-certificate-pem-file-legacy)
* [Upload .pem file to MoEngage Dashboard](/docs/developer-guide/ios-sdk/push/basic/apns-certificate-pem-file-legacy)
Follow the links on each step to complete it.
## Project Capability Changes
Once the APNS Certificate is uploaded, enable Push Entitlement in the Xcode project. For that select your app target, then go to Capabilities. Here enable the Push Notifications capability for your app. Also, we make use of silent pushes to track uninstalls. For tracking uninstalls of all the users, enable Remote Notification background mode in the app's capabilities as shown below:
## Push Registration
After this, you will have to register for push notification by using the **registerForPushNotification()** method of the plugin, as shown below:
```auto Dart theme={null}
import 'package:moengage_flutter/moengage_flutter.dart';
final MoEngageFlutter _moengagePlugin = MoEngageFlutter(YOUR_WORKSPACE_ID);
_moengagePlugin.initialise();
_moengagePlugin.registerForPushNotification();
```
## Provisional Push Registration:
This feature is supported from version ***9.0.0*** of the plugin.
To register for provisional push notification, call [***registerForProvisionalPush()***](https://pub.dev/documentation/moengage_flutter/latest/moengage_flutter/MoEngageFlutter/registerForProvisionalPush.html) API of the plugin as shown below.
```auto Dart theme={null}
import 'package:moengage_flutter/moengage_flutter.dart';
MoEngageFlutter _moengagePlugin = MoEngageFlutter(YOUR_WORKSPACE_ID);
_moengagePlugin.initialise();
_moengagePlugin.registerForProvisionalPush();
```
## Rich Push and Templates Support
To support Rich Push (images/videos/audio in the notification) and Templates in your Flutter app, set up the iOS Notification Service and Content Extensions:
* [Integrate Service and Content Extension](developer-guide/flutter-sdk/push/basic/integrating-moengage-service-and-content-extension-in-ios/set-up-ios-notification-extensions-for-rich-push-and-impression-tracking)
Or you can manually integrate Rich Push and Push Templates. For more information, refer the below docs:
* [Rich Push](/docs/developer-guide/ios-sdk/push/basic/ios-push-integration-tutorial)
* [Push Templates](/docs/developer-guide/ios-sdk/push/optional/push-templates)
# Push Callback
Source: https://moengage.com/docs/developer-guide/flutter-sdk/push/basic/push-callback
Register for push click callbacks in your Flutter app to handle notification interactions with MoEngage.
## Push Click Callback
MoEngage's Flutter plugin optionally provides a callback on push clicks.
To register for the callback, call the **setPushClickCallbackHandler()** on the **MoEngageFlutter** object in your dart code.\
This API takes a method as input with whose **typedef** is **PushClickCallbackHandler(PushCampaignData data).**
```Dart Dart theme={null}
import 'package:moengage_flutter/moengage_flutter.dart';
void _onPushClick(PushCampaignData message) {
print("_onPushClick(): Push click callback from native to flutter. Payload " +
message.toString());
}
final MoEngageFlutter _moengagePlugin = MoEngageFlutter(YOUR_WORKSPACE_ID);
_moengagePlugin.setPushClickCallbackHandler(_onPushClick);
```
Make sure this callback is set as soon as the application is initialized. Preferably in the **initState()** of your application widget.
Make sure the callback is set before calling the initialize **()** of the MoEngage Plugin.
## Payload
NotificationPayload received in the callback is an `PushCampaignData` instance with the following definition:
```Dart Dart theme={null}
class PushCampaignData {
Platforms platform;
AccountMeta accountMeta;
PushCampaign data;
}
```
**platform** - Native platform from which callback is triggered. Possible values - **android**, **ios**. **data**- **PushCampaignData** object
```Dart Dart theme={null}
class PushCampaign {
bool isDefaultAction;
Map clickedAction;
Map payload;
}
```
**isDefaultAction** - This key is present only for the Android Platform. It's a boolean value indicating if the user clicked on the default content or not. true if the user clicks on the default content else false.
**clickedAction**- Action to be performed on notification click.
Payload Structure for **clickedAction** Map
```json JSON theme={null}
{
"clickedAction": {
"type": "navigation/customAction",
"payload": {
"type": "screenName/deepLink/richLanding",
"value": "",
"kvPair": {
"key1": "value1",
"key2": "value2",
...
}
}
}
}
```
**clickedAction.type**- Type of click action. Possible values **navigation** and **customAction**. Currently, **customAction** is supported only on Android.\
**clickAction.payload** - Action payload for the clicked action.\
**clickedAction.payload.type** - Type of navigation action defined. Possible values **screenName**, **deepLink**, and **richLanding**.
Currently, in the case of iOS, rich landing and deep-link URLs are processed internally by the SDK and not passed in this callback; therefore possible value in the case of iOS is only **screenName**.\
**clickAction.value** - value entered for navigation action or custom payload.\
**clickAction.kvPair** - Custom key-value pair entered on the MoEngage Platform.\
**payload** - Complete campaign payload.
## Android Payload
If the user clicks on the default content of the notification, the key-value pair and campaign payload can be found inside the **payload** key. If the user clicks on the action button or a push template action, the action payload would be found inside **clickedAction**.\
You can use the **isDefaultAction** key to check whether the user clicked on the default content and then parse the payload accordingly.
## iOS Payload
In the case of iOS, you would always receive the key-value pairs for clicked action in the **clickedAction** property. Refer to this [link](/docs/developer-guide/ios-sdk/push/advanced/custom-notification-handling) to knowing the iOS notification payload structure.
## Self-Handled Push Click Android (Optional)
By default, when the user clicks on a notification the SDK redirects the user to the defined Activity and passes the callback to the Application to load the specific flutter component.
When the application is in the foreground it might seem like the application is reloading and not a very good user experience. You might just want to navigate the user to the specific flutter component. In order to handle the push click by yourself when the Application is in the foreground follow the below steps.
While initializing the Flutter Plugin, enable foreground click callback in the ***PushConfig*** object.
```Dart Dart theme={null}
import 'package:moengage_flutter/moengage_flutter.dart';
...
final MoEngageFlutter _moengagePlugin = MoEngageFlutter(
"YOUR_WORKSPACE_ID",
moEInitConfig: MoEInitConfig(
pushConfig: PushConfig(
shouldDeliverCallbackOnForegroundClick: true)
)
);
```
**Android Configuration**
Enable the **lifecycleAwareCallback** flag in the SDK initialization in the Application class as shown below.
```kotlin Kotlin wrap theme={null}
val moEngage = MoEngage.Builder(this, "YOUR_WORKSPACE_ID")
MoEInitializer.initialiseDefaultInstance(
context = applicationContext,
builder = moEngage,
lifecycleAwareCallbackEnabled = true)
```
```java Java wrap theme={null}
MoEngage.Builder moEngage = new MoEngage.Builder(this, "YOUR_WORKSPACE_ID");
MoEInitializer.INSTANCE.initializeDefaultInstance(getApplicationContext(), moEngage, true);
```
You must call the ***initialize()*** whenever the Application comes to the foreground by adding **WidgetsBindingObserver** in the root widget of your app.
So you would need to call **initialise()** in two places, one in **initState()** as usual and another time in **didChangeAppLifecycleState()** in the root widget of your app.
```Dart Dart wrap theme={null}
class MyApp extends StatefulWidget {
@override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State with WidgetsBindingObserver {
final MoEngageFlutter _moengagePlugin = MoEngageFlutter("");
@override
void initState() {
super.initState();
_moengagePlugin.initialise(); // Initialise MoEngage SDK
WidgetsBinding.instance.addObserver(this);
}
@override
void didChangeAppLifecycleState(AppLifecycleState state) {
super.didChangeAppLifecycleState(state);
if (state == AppLifecycleState.resumed) {
_moengagePlugin.initialise(); //Call initialise() again in on App Resume State
}
}
}
```
Handle the redirection as shown below:
```Dart Dart wrap theme={null}
void _onPushClick(PushCampaignData message) {
if (message.data.selfHandledPushRedirection) {
// Handle Redirection for Deeplinking or ScreenName
} else {
// Callback After SDK Handled Redirection
}
}
```
Add the below Activity to your AndroidManifest.xml under **application** tag.
```xml XML wrap theme={null}
```
# Location Triggered
Source: https://moengage.com/docs/developer-guide/flutter-sdk/push/optional/location-triggered
Add location-triggered geofence push notifications to your Flutter app using the MoEngage plugin.

# Installation
To add MoEngage Geofence SDK to your application, edit the application's **pubspec.yaml** file and add the below dependency to it:
```pubspec.yaml pubspec.yaml theme={null}
dependencies:
moengage_geofence: $latestVersion
```
***\$latestVersion*** refers to the latest version of the plugin.
Post including the dependency, run ***flutter pub get*** command in the terminal to install the dependency.
After installing the plugin, use the following platform-specific configuration.
This plugin is dependent on **moengage\_flutter** plugin. Make sure you have installed the **moengage\_flutter** plugin as well. Refer to the [doc](/docs/developer-guide/flutter-sdk/sdk-integration/sdk-installation/framework-dependency) for the same.
For location-triggered push to work, ensure your Application has:
* Location permission
* Play Services Location Library
* The device's location should be enabled
## Android Installation
Starting *****moengage\_geofence5.0.0***** the plugin includes the native dependency, so there is no need to include any additional native dependency.
## iOS Installation
In the case of iOS, the native dependency is part of the Geofence flutter SDK itself, so there is no need to include any additional dependency for supporting Geofence.
## Configuration
### Start Geofence Monitoring
After integrating the geofence package call **startGeofenceMonitoring()** method to initiate the geofence module, this will fetch the geofences around the current location of the user. Please take a look at the [iOS doc](/docs/developer-guide/ios-sdk/push/optional/location-triggered) and [Android doc](/docs/developer-guide/android-sdk/push/optional/location-triggered) for more information on Geofence. By default, the geofence feature is not enabled. You need to call **startGeofenceMonitoring()** to receive location-triggered push messages.
```Dart Dart theme={null}
import 'package:moengage_geofence/moengage_geofence.dart';
final MoEngageGeofence _moEngageGeofence = MoEngageGeofence('YOUR_WORKSPACE_ID');
@override
void initState() {
super.initState();
// Starts geofence monitoring
_moEngageGeofence.startGeofenceMonitoring();
}
```
Call `startGeofenceMonitoring()` only after the SDK is initialized and location permission has been granted. If the SDK is not initialized, the call is ignored silently.
### Stop Geofence Monitoring
If you want to stop the geofence monitoring or feature use the **stopGeofenceMonitoring()** API. This API will remove the existing geofences.
```Dart Dart theme={null}
import 'package:moengage_geofence/moengage_geofence.dart';
final MoEngageGeofence _moEngageGeofence = MoEngageGeofence('YOUR_WORKSPACE_ID');
// Stops geofence monitoring and removes the existing geofences
_moEngageGeofence.stopGeofenceMonitoring();
```
# Notification Center
Source: https://moengage.com/docs/developer-guide/flutter-sdk/push/optional/notification-center
Add a notification center inbox to your Flutter app using the MoEngage inbox plugin.

# Installation
To add the MoEngage Inbox SDK to your application, edit your application's **pubspec.yaml** file and add the below dependency to it:
```pubspec.yaml pubspec.yaml theme={null}
dependencies:
moengage_inbox: $latestVersion
```
Run flutter packages get to install the SDK.
This plugin is dependent on **moengage\_flutter** plugin. Make sure you have installed the **moengage\_flutter** plugin as well. Refer to the [doc](/docs/developer-guide/flutter-sdk/sdk-integration/sdk-installation/framework-dependency) for the same.
## Android Installation
Once you install the Flutter Plugin add MoEngage's native Android SDK dependency to the Android project of your application.
Navigate to **android/app/build.gradle**. Add the MoEngage Android SDK's dependency in the **dependencies** block.
```json build.gradle wrap theme={null}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation("com.moengage:inbox-core:$sdkVersion")
}
```
where **\$sdkVersion** should be replaced by the latest version of the MoEngage SDK.
## iOS Installation
In the case of iOS, the native dependency is part of the core SDK itself, so there is no need to include any additional dependency for supporting Notification Center.
Make sure to configure [AppGroup ID in App Target](/docs/developer-guide/ios-sdk/push/basic/ios-push-integration-tutorial) and Set up [Notification Service Extension](/docs/developer-guide/ios-sdk/push/basic/ios-push-integration-tutorial#test-notification-delivery-and-display) in your iOS Project, for the SDK to save the received notifications.
# Fetch Messages
To fetch all the inbox messages use **fetchAllMessages()** method as shown below, where you would get an instance of **InboxData**.
```Dart Dart theme={null}
import 'package:moengage_inbox/moengage_inbox.dart';
MoEngageInbox _moEngageInbox = MoEngageInbox(YOUR_WORKSPACE_ID);
InboxData data = await _moEngageInbox.fetchAllMessages();
```
## InboxData Payload
InboxData will be received in the below format:
```Dart Dart theme={null}
class InboxData {
/// Native platform from which the callback was triggered.
String platform;
/// List of [InboxMessage]
List messages;
}
class InboxMessage {
/// internal identifier used by the SDK for storage.
int id;
/// Unique identifier for a message.
String campaignId;
/// Text content of the message. Instance of [TextContent]
TextContent textContent;
/// true if the message has been clicked by the user else false
bool isClicked;
/// Media content associated with the message.
Media? media;
/// List of actions to be executed on click.
List action;
/// Tag associated to the message.
String tag;
/// The time in which the message was received on the device.
///
/// Format - ISO-8601 yyyy-MM-dd'T'HH:mm:ss'Z'
String receivedTime;
/// The time at which the message expiry.
///
/// Format - ISO-8601 yyyy-MM-dd'T'HH:mm:ss'Z'
String expiry;
/// Complete message payload.
Map payload;
/// A key representing the group to which the inbox message belongs.
/// @since 9.0.0
String groupKey;
/// Notification Replacement Id.
/// @since 9.0.0
String notificationId;
/// The timestamp indicating when the message was sent.
///
/// Format - ISO-8601 yyyy-MM-dd'T'HH:mm:ss'Z'
/// @since 9.0.0
String sentTime;
}
class TextContent {
/// Title string for the inbox message.
String title;
/// Message string for the inbox message.
String message;
/// Summary string for the inbox message.
///
/// Note: This is present for Android Platform.
String summary;
/// Subtitle string for the inbox message.
///
/// Note: This is present only for the iOS Platform.
String subtitle;
}
class Media {
/// Content type of the Media.
MediaType mediaType;
/// Url for the media content. Generally a http(s) url.
String url;
/// Defines the accessibility properties for the media model
/// @since 9.0.0
AccessibilityData? accessibilityData;
}
class AccessibilityData {
/// Text for the AccessibilityData
String? text;
/// Hint for the AccessibilityData. Applicable only for iOS Platform
String? hint;
}
class Action {
/// ActionType - navigation
ActionType actionType;
}
class NavigationAction extends Action {
///navigation type deepLink/richLanding/screenName
NavigationType navigationType;
String value;
Map kvPair;
}
```
# Get Unclicked Message Count
To obtain the unclicked messages count from the Inbox use **getUnclickedCount()** method as shown below:
```Dart Dart theme={null}
MoEngageInbox _moEngageInbox = MoEngageInbox(YOUR_WORKSPACE_ID);
int count = await _moEngageInbox.getUnClickedCount();
```
## Track Message Clicks:
To track clicks on the messages inside your Inbox use **trackMessageClicked()** method as shown below:
```Dart Dart theme={null}
MoEngageInbox _moEngageInbox = MoEngageInbox(YOUR_APP_D);
_moEngageInbox.trackMessageClicked(message); //Pass the instance of InboxMessage here
```
## Delete Message:
To delete a particular message from the list of messages use **deleteMessage()** method as shown below:
```Dart Dart theme={null}
MoEngageInbox _moEngageInbox = MoEngageInbox(YOUR_WORKSPACE_ID);
_moEngageInbox.deleteMessage(message); //Pass the instance of InboxMessage here
```
The hybrid framework does not support the MoEngage default notification center. Only the Self-handled Notification center is supported.
# Flutter Sample App
Source: https://moengage.com/docs/developer-guide/flutter-sdk/sample-app/flutter-sample-app
Explore the MoEngage Flutter sample app on GitHub as a reference for integrating the SDK.
The [MoEngage Flutter Sample application](https://github.com/moengage/Flutter-SDK) offers a useful reference point for integrating MoEngage into your Flutter app.
## Next Steps
* [SDK Installation](/docs/developer-guide/flutter-sdk/sdk-integration/sdk-installation/framework-dependency)
* [Framework Initialization](/docs/developer-guide/flutter-sdk/sdk-integration/sdk-initialization/manual-initialization/framework-initialization)
# JWT Authentication
Source: https://moengage.com/docs/developer-guide/flutter-sdk/sdk-integration/advanced/jwt-authentication
Secure your MoEngage data collection by implementing JWT authentication in your Flutter application.
## Overview
JWT (JSON Web Token) authentication is a standard method for securely verifying user identity. By implementing JWT authentication, you add a critical layer of security to your data collection process with MoEngage.
The feature ensures that the data sent on behalf of your identified users is authentic and has not been tampered with. This security is achieved by requiring a token that is cryptographically signed by your own server, which prevents unauthorized users from impersonating your legitimate users.
**Prerequisites**
Before you begin the implementation, ensure you meet the following requirements:
* Your application must use the MoEngage Flutter Core plugin version ***11.0.0*** or higher to access the JWT authentication feature.
* You must have access to your MoEngage dashboard to manage public keys and configure the feature's enforcement settings. For detailed information on enforcement settings, [refer here](/docs/user-guide/settings/account/security/sdk-authentication#step-2-select-an-enforcement-mode).
The following diagram illustrates the interaction between your application, your server, the MoEngage SDK, and the MoEngage server:
## Integration
Perform the following to integrate JWT authentication into your Flutter application.
### Step 1: Enable JWT Authentication
Enable JWT authentication during native SDK initialization on each platform. Follow the instructions that match the initialization method your application uses. Steps 2 and 3 are the same for both methods.
If you use the [config generator](https://app-cdn.moengage.com/sdk/integration/config/index.html) to produce your configuration files, set **Enable JWT Authorisation** to **Yes**. The generated files then contain the keys described below.
#### Android
**Manual Initialization**
Configure the ***NetworkAuthorizationConfig*** property on the ***MoEngage.Builder*** object. For more information, refer to [Android SDK Initialization](/docs/developer-guide/flutter-sdk/sdk-integration/sdk-initialization/manual-initialization/android-sdk-initialization).
```kotlin Kotlin wrap theme={null}
import com.moengage.core.DataCenter
import com.moengage.core.MoEngage
import com.moengage.core.config.NetworkAuthorizationConfig
import com.moengage.core.config.NetworkRequestConfig
import com.moengage.flutter.MoEInitializer
val moEngage = MoEngage.Builder(this, "YOUR_WORKSPACE_ID", DataCenter.DATA_CENTER_X)
.configureNetworkRequest(NetworkRequestConfig(NetworkAuthorizationConfig(isJwtEnabled = true)))
MoEInitializer.initialiseDefaultInstance(context = this, builder = moEngage)
```
```java Java wrap theme={null}
import com.moengage.core.DataCenter;
import com.moengage.core.MoEngage;
import com.moengage.core.config.NetworkAuthorizationConfig;
import com.moengage.core.config.NetworkRequestConfig;
import com.moengage.flutter.MoEInitializer;
MoEngage.Builder builder = MoEngage.builder(this, "YOUR_WORKSPACE_ID", DataCenter.getDataCenterX())
.configureNetworkRequest(new NetworkRequestConfig(new NetworkAuthorizationConfig(true)));
MoEInitializer.initialiseDefaultInstance(this, builder);
```
**File-Based Initialization**
Add the following key to your `moengage.xml` configuration file. For more information, refer to [File Based Initialization](/docs/developer-guide/flutter-sdk/sdk-integration/sdk-initialization/file-based-initialization/file-based-initialization#android-configuration-reference).
```xml moengage.xml theme={null}
true
```
#### iOS
**Manual Initialization**
Configure the ***networkConfig*** property on the ***MoEngageSDKConfig*** object. For more information, refer to [iOS SDK Initialization](/docs/developer-guide/flutter-sdk/sdk-integration/sdk-initialization/manual-initialization/ios-sdk-initialization).
```swift Swift wrap theme={null}
let sdkConfig = MoEngageSDKConfig(appId: "YOUR_WORKSPACE_ID", dataCenter: .YOUR_DATA_CENTER)
sdkConfig.networkConfig = MoEngageNetworkRequestConfig(authorizationConfig: MoEngageNetworkAuthorizationConfig(isJwtEnabled: true))
MoEngageInitializer.sharedInstance.initializeDefaultInstance(sdkConfig, launchOptions: launchOptions)
```
```objectivec Objective-C wrap theme={null}
MoEngageSDKConfig* sdkConfig = [[MoEngageSDKConfig alloc] initWithAppId:@"YOUR_WORKSPACE_ID" dataCenter:YOUR_DATA_CENTER];
sdkConfig.networkConfig = [[MoEngageNetworkRequestConfig alloc] initWithAuthorizationConfig:[[MoEngageNetworkAuthorizationConfig alloc] initWithIsJwtEnabled:YES]];
[[MoEngageInitializer sharedInstance] initializeDefaultInstance:sdkConfig launchOptions:launchOptions];
```
**File-Based Initialization**
Add the following key to the `MoEngage` dictionary in your `Info.plist`. For more information, refer to [File Based Initialization](/docs/developer-guide/flutter-sdk/sdk-integration/sdk-initialization/file-based-initialization/file-based-initialization#ios-configuration-reference).
```xml Info.plist theme={null}
IsJwtEnabled
```
### Step 2: Pass the JWT to the SDK
Your application is responsible for managing the JWT lifecycle. The recommended flow is to fetch a token when the user logs in and pass the token to the SDK. You should also check whether the token has expired on subsequent app launches and fetch a new one if necessary.
Use the ***passAuthenticationDetails()*** method on the ***MoEngageFlutter*** object to provide the token to the SDK.
```dart Dart wrap theme={null}
import 'package:moengage_flutter/moengage_flutter.dart';
final MoEngageFlutter _moengagePlugin = MoEngageFlutter(YOUR_WORKSPACE_ID);
_moengagePlugin.passAuthenticationDetails(
AuthenticationDetailsRequest(
authenticationType: AuthenticationType.jwt,
data: JwtAuthenticationData(
token: 'YOUR_JWT_TOKEN',
userIdentifier: 'USER_IDENTIFIER',
),
),
);
```
For detailed information, refer to [Classes and Enums](#classes-and-enums).
### Step 3: Register the Callback Handler and Handle Authentication Errors
The SDK delivers token validation errors returned by the MoEngage server through a callback. Register a handler using ***setAuthenticationErrorCallbackHandler()*** so your application can fetch and provide a new token when authentication fails. This method takes a function whose `typedef` is `AuthenticationErrorCallbackHandler(AuthenticationErrorData data)`.
Register the handler in a global scope, such as the `initState()` of your root widget, so your application always receives callbacks.
```dart Dart wrap theme={null}
import 'package:moengage_flutter/moengage_flutter.dart';
final MoEngageFlutter _moengagePlugin = MoEngageFlutter(YOUR_WORKSPACE_ID);
_moengagePlugin.setAuthenticationErrorCallbackHandler(_onAuthenticationError);
void _onAuthenticationError(AuthenticationErrorData data) {
if (data.authenticationType == AuthenticationType.jwt) {
final JwtAuthenticationErrorData errorData =
data.data as JwtAuthenticationErrorData;
final JwtErrorCode jwtError = errorData.code;
final String message = errorData.message;
// Take appropriate action based on jwtError.
// For example, fetch a new token and call passAuthenticationDetails() again.
}
}
```
To stop receiving the callback, pass `null` to the same method.
```dart Dart wrap theme={null}
_moengagePlugin.setAuthenticationErrorCallbackHandler(null);
```
For detailed information, refer to [Classes and Enums](#classes-and-enums).
## Classes and Enums
The following classes and enums define the data structures used by the JWT authentication methods described in this guide. Use them when constructing your token payload and handling errors.
```dart Dart wrap theme={null}
// Payload accepted by passAuthenticationDetails().
class AuthenticationDetailsRequest {
AuthenticationDetailsRequest({
required this.authenticationType,
required this.data,
});
AuthenticationType authenticationType;
AuthenticationDetails data; // For JWT, use JwtAuthenticationData.
}
// Authentication scheme used to authenticate the SDK's network requests.
enum AuthenticationType { jwt }
// JWT specific authentication payload.
final class JwtAuthenticationData extends AuthenticationDetails {
JwtAuthenticationData({
required this.token,
required this.userIdentifier,
});
String token;
String userIdentifier;
}
// Payload delivered to AuthenticationErrorCallbackHandler.
class AuthenticationErrorData {
AuthenticationErrorData({
required this.platform,
required this.accountMeta,
required this.authenticationType,
required this.data,
});
Platforms platform;
AccountMeta accountMeta;
AuthenticationType authenticationType;
AuthenticationErrorDetails data; // For JWT, use JwtAuthenticationErrorData.
}
// JWT specific error details.
final class JwtAuthenticationErrorData extends AuthenticationErrorDetails {
JwtAuthenticationErrorData({
required this.code,
required this.token,
required this.userIdentifier,
required this.message,
});
JwtErrorCode code;
String token;
String userIdentifier;
String message;
}
// Reason the JWT authentication failed.
enum JwtErrorCode {
timeConstraintFailure,
decryptionFailed,
headerTypeIncompatible,
payloadContentMissing,
invalidSignature,
identifierMismatch,
unknown,
tokenNotAvailable,
}
```
**Information**
* If an API request fails due to an authentication error, the SDK will not retry the request until your application provides a new token.
* After 10 consecutive authentication failures in a single session, the SDK will stop attempting to sync data until the next session begins. This counter resets after any successful sync.
* Upon user logout, if a data sync fails due to a JWT error, the pending data will be deleted, and no retry will be attempted.
# Limitations
Source: https://moengage.com/docs/developer-guide/flutter-sdk/sdk-integration/limitations
Review unsupported features and native integration requirements when using the MoEngage Flutter plugin.
Compared to the Native Android or iOS SDKs there are a certain set of features we either do not support or require native Android or iOS implementation when using our Flutter plugin.
# Features not supported
* Action Buttons in iOS Notifications
# File Based Initialization
Source: https://moengage.com/docs/developer-guide/flutter-sdk/sdk-integration/sdk-initialization/file-based-initialization/file-based-initialization
Configure MoEngage Flutter SDK initialization using native configuration files instead of code.
## Overview
Starting with v10.3.0, the Flutter SDK supports file-based initialization.
To streamline the integration process and minimize initialization errors, MoEngage supports Script-Based Initialization. This approach allows you to manage App IDs and configuration settings directly within native configuration files, keeping them separate from your application logic.
This article outlines how you can use the form-based interface to generate a validated code snippet for initialization and access module-specific configurations.
Alternatively, the SDK can be initialized manually. If you require this approach, please refer to the guide on [Framework Initialization](/docs/developer-guide/flutter-sdk/sdk-integration/sdk-initialization/manual-initialization/framework-initialization).
Follow these steps to generate your initialization script:
1. Navigate to the [Initialization Website.](https://app-cdn.moengage.com/sdk/integration/config/index.html)
2. Configure the values based on your application requirements. Refer to the Configuration Parameters tables below:
* [Android](#android-configuration-reference)
* [iOS](#ios-configuration-reference)
3. Click **Generate Code** at the bottom of the form.
## Step 1: Android Configuration (XML)
For Android, initialization is handled by placing an XML configuration file in the application's resource directory.
### Android Configuration Reference
Below is the comprehensive list of keys available for `moengage.xml`.
| Category | XML Key Name | Type | Description |
| :----------- | :---------------------------------------------------- | :------- | :---------------------------------------------------------------------------------------------- |
| **Core** | `com_moengage_core_workspace_id` | String | Specifies your Workspace ID. This field is mandatory. |
| | `com_moengage_core_file_based_initialisation_enabled` | Boolean | Set to `true` to enable this feature. |
| | `com_moengage_core_data_center` | Integer | Default: `1`. For more info, refer [Data Center values](#data-center-values). |
| | `com_moengage_core_environment` | String | Supported values are: `default`, `live`, or `test`. |
| | `com_moengage_core_custom_base_domain` | String | Specifies the base custom proxy domain to route SDK network traffic through your own subdomain. |
| | `com_moengage_core_integration_partner` | String | Specifies the core integration partner. For example, `segment` or `mparticle`. |
| **Push** | `com_moengage_push_notification_small_icon` | Drawable | Resource ID for small icon. |
| | `com_moengage_push_notification_large_icon` | Drawable | Resource ID for large icon. |
| | `com_moengage_push_notification_color` | Color | Notification accent color. |
| | `com_moengage_push_notification_token_retry_interval` | Integer | Retry interval (in seconds) for token registration. |
| | `com_moengage_push_kit_registration_enabled` | Boolean | If `true`, SDK registers for push token. |
| **Logs** | `com_moengage_core_log_level` | Integer | `0` (No Log) to `5` (Verbose). Default: `3`. |
| | `com_moengage_core_log_enabled_for_release_build` | Boolean | If `true`, prints logs in release builds. |
| **Security** | `com_moengage_core_storage_encryption_enabled` | Boolean | Enables local storage encryption. |
| | `com_moengage_core_network_encryption_enabled` | Boolean | Enables payload encryption over the network. |
| **Sync** | `com_moengage_core_periodic_data_sync_enabled` | Boolean | Enables periodic data sync in the foreground. |
| | `com_moengage_core_background_data_sync_enabled` | Boolean | Enables periodic data sync in the background. |
| **In-App** | `com_moengage_inapp_show_in_new_activity_enabled` | Boolean | Required for specific TV/Android setups. |
**Troubleshooting**
If the XML file is missing or the `com_moengage_core_workspace_id` is empty, the SDK will throw a `ConfigurationMismatchError`.
### Add Configuration File
Place the generated file in `android/app/src/main/res/values/`.
## Step 2: iOS Configuration (Info.plist)
For iOS, initialization is handled by adding a configuration dictionary to your `Info.plist`.
### iOS Configuration Reference
Below is the comprehensive list of keys available for the `MoEngage` dictionary.
| Category | Plist Key | Type | Description |
| :----------- | :----------------------------------- | :--------------- | :---------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Core** | `WorkspaceId` | String | Specifies your Workspace ID. It is a Mandatory field. |
| | `IsSdkAutoInitialisationEnabled` | Boolean | Set to `true` to enable SDK auto initialisation. |
| | `DataCenter` | Integer | Specifies the Data Center value. This is a Mandatory field. The default value is *1*. For more info, refer to [Data Center values](#data-center-values). |
| | `IsTestEnvironment` | String / Boolean | Customer selected option (`true`/`false`). Default value is: `$(SWIFT_ACTIVE_COMPILATION_CONDITIONS)`. |
| | `CustomBaseDomain` | String | Specifies the base custom proxy domain to route SDK network traffic through your own subdomain. |
| | `IntegrationPartner` | String | Specifies your integration partner. For example, `segment` or `mparticle`.Default value: none. |
| | `AppGroupName` | String | Specifies the App Group name used for sharing SDK data. Default value: `""`. |
| **Logs** | `IsLoggingEnabled` | Boolean | Set to *true* to enable SDK logs. |
| | `Loglevel` | Integer | `0` to `5`. Default: `2`. |
| **Security** | `IsStorageEncryptionEnabled` | Boolean | Enables local storage encryption. Default value: `false`. |
| | `KeychainGroupName` | String | Specifies the keychain group name used for storing encryption keys. This is a mandatory field if `IsStorageEncryptionEnabled` is `true`. Default value: `""`. |
| | `IsNetworkEncryptionEnabled` | Boolean | Enables payload encryption. Default: `false`. |
| | `EncryptionEncodedTestKey` | String | Dashboard auto-populated string. Used if `IsNetworkEncryptionEnabled` is `true`. |
| | `EncryptionEncodedLiveKey` | String | Dashboard auto-populated string. Used if `IsNetworkEncryptionEnabled` is `true`. |
| **Sync** | `AnalyticsEnablePeriodicFlush` | Boolean | Enables periodic data flush. Default: `true`. |
| | `AnalyticsPeriodicFlushDuration` | Integer | Flush interval in seconds. Default: `60`. |
| **In-App** | `InAppDisplaySafeAreaInset` | Real | Decimal value representing safe area padding. Default: `0`. |
| | `InAppShouldProvideDeeplinkCallback` | Boolean | If `true`, provides callback on deeplink. Default: `false`. |
### Data Center Values
Configure the integer corresponding to your region. Incorrect values will result in data loss.
| Data Center | Dashboard host |
| ----------- | ------------------------------------------------------------- |
| 1 | [dashboard-01.moengage.com](http://dashboard-01.moengage.com) |
| 2 | [dashboard-02.moengage.com](http://dashboard-02.moengage.com) |
| 3 | [dashboard-03.moengage.com](http://dashboard-03.moengage.com) |
| 4 | [dashboard-04.moengage.com](http://dashboard-04.moengage.com) |
| 5 | [dashboard-05.moengage.com](http://dashboard-05.moengage.com) |
| 6 | [dashboard-06.moengage.com](http://dashboard-06.moengage.com) |
### Update Info.plist
1. Open your project's `Info.plist` ( found in `ios/Runner/` ).
2. Create a new Top-Level Key named `MoEngage` of type `Dictionary`.
3. Add the configuration file content generated in the [Initialization Website.](https://app-cdn.moengage.com/sdk/integration/config/index.html)
The key `IsSdkAutoInitialisationEnabled` uses the British spelling ('s'). Ensure you use the exact key name shown below, or initialization will fail.
**XML Snippet Representation:**
```xml XML theme={null}
MoEngageWorkspaceIdYOUR_WORKSPACE_IDIsTestEnvironment$(SWIFT_ACTIVE_COMPILATION_CONDITIONS)|$(GCC_PREPROCESSOR_DEFINITIONS)DataCenter1CustomBaseDomaindata.example.comIsLoggingEnabled
```
## Step 3: Framework Level Initialization
After you configure the native files, the initialization code in your hybrid framework is simplified.
Create **MoEngageFlutter(YOUR\_Workspace\_ID)** object and in the project's App Widget call **initialise()** of **MoEngageFlutter** plugin in the **initState()** the method as shown below:
```dart Dart theme={null}
import 'package:moengage_flutter/moengage_flutter.dart';
final MoEngageFlutter _moengagePlugin = MoEngageFlutter(YOUR_WORKSPACE_ID);
@override
void initState() {
super.initState();
initPlatformState();
_moengagePlugin.initialise();
}
```
## Step 4: Migration and Precedence
To migrate from manual code-based initialization to file-based approach, refer [here](/docs/developer-guide/flutter-sdk/sdk-integration/sdk-initialization/file-based-initialization/migration-and-precedence).
## Step 5: Environments (Test vs. Live)
You can configure Test/Live environments within these files.
* **Android:** Use the key `test`.
* **iOS:** Use `IsTestEnvironment`
# Migration and Precedence
Source: https://moengage.com/docs/developer-guide/flutter-sdk/sdk-integration/sdk-initialization/file-based-initialization/migration-and-precedence
Migrate your MoEngage Flutter SDK from code-based initialization to file-based configuration.
### Android Migration Steps
To migrate from manual code-based initialization to the XML file-based approach, follow the below steps:
1. **Add Configuration File:** Place the generated `moengage.xml` file in `android/app/src/main/res/values/`.
2. **Update Application Class:** Remove the existing initialization code (the manual `MoEngage.Builder` logic) from your Application class and replace it with the default instance initialization to enable reading from the XML file.
| Code Language | Existing Code | Replace with |
| ------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------- |
| Kotlin | val moEngage: MoEngage.Builder = MoEngage.Builder(this,"YOUR\_WORKSPACE\_ID", DataCenter.DATA\_CENTER\_X) MoEInitializer.initialiseDefaultInstance(context = this, builder = moEngage) | MoEInitializer.initializeDefaultInstance(application) |
| Java | MoEngage.Builder moEngage = new MoEngage.Builder(this, "YOUR\_WORKSPACE\_ID", YOUR\_DATA\_CENTER); MoEInitializer.initialiseDefaultInstance(this, moEngage); | MoEInitializer.INSTANCE.initializeDefaultInstance(this); |
### iOS Migration Steps
To migrate from code-based initialization to the `Info.plist` based approach, follow these steps:
1. **Update Info.plist**: Add the required MoEngage configuration keys (e.g., WorkspaceId, DataCenter) inside the *MoEngage* key in your `Info.plist` file.
2. **Update AppDelegate**: Remove the existing initialization code (the manual MoEngageSDKConfig logic) from your `AppDelegate` class and replace it with the default instance initialization to enable reading from the `Info.plist` file.
| Code Language | Existing Code | Replace with |
| ------------- | ------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------- |
| Swift | MoEngageInitializer.sharedInstance.initializeDefaultInstance(sdkConfig, launchOptions: launchOptions) | MoEngageInitializer.sharedInstance.initializeDefaultInstance() |
| Objective-C | \[\[MoEngageInitializer sharedInstance] initializeDefaultInstance:sdkConfig launchOptions:launchOptions]; | \[MoEngageInitializer.sharedInstance initializeDefaultInstance]; |
### Precedence Rules
The source of configuration is determined by the initialization function called in your native code:
* **Android**:
* **File-Based Init:** Calling `MoEInitializer.initializeDefaultInstance(context)` instructs the SDK to look for and read the `moengage.xml` file.
* **Code-Based Init:** Calling `MoEInitializer.initialize(context, moEngage.Builder)` will initialize the SDK using the configuration object passed in the parameters, ignoring the XML file even if it exists.
* **iOS:** Auto-initialization (via `Info.plist`) occurs first. However, if you subsequently call the manual `initialize` method with a configuration object in your code, it will update the current instance configuration.
# Android SDK Initialization
Source: https://moengage.com/docs/developer-guide/flutter-sdk/sdk-integration/sdk-initialization/manual-initialization/android-sdk-initialization
Initialize the MoEngage Flutter SDK in your Android application class with your workspace ID.
Get the APP ID from the Settings Page \_Dashboard --> Settings --> App --> General Settings on the MoEngage dashboard and initialize the MoEngage SDK in the ***Application*** class's ***onCreate()***.
Initialize the SDK on the main thread inside onCreate() and not create a worker thread and initialize the SDK on that thread.
```kotlin Kotlin theme={null}
import com.moengage.flutter.MoEInitializer
import com.moengage.core.MoEngage
import com.moengage.core.DataCenter
...
// `this` is the instance of the application class and "YOUR_WORKSPACE_ID" is the Workspace ID from the dashboard.
val moEngage: MoEngage.Builder = MoEngage.Builder(this,"YOUR_WORKSPACE_ID", DataCenter.DATA_CENTER_X)
MoEInitializer.initialiseDefaultInstance(context = this, builder = moEngage)
```
```java Java theme={null}
import com.moengage.flutter.MoEInitializer;
import com.moengage.core.MoEngage;
import com.moengage.core.DataCenter;
...
// `this` is the instance of the application class and "YOUR_WORKSPACE_ID" is the Workspace ID from the dashboard.
MoEngage.Builder moEngage = new MoEngage.Builder(this, "YOUR_WORKSPACE_ID", YOUR_DATA_CENTER);
MoEInitializer.initialiseDefaultInstance(this, moEngage);
```
Following details of the different data centers you need to set based on the dashboard hosts
| Data Center | Dashboard host |
| -------------------------- | ------------------------- |
| DataCenter.DATA\_CENTER\_1 | dashboard-01.moengage.com |
| DataCenter.DATA\_CENTER\_2 | dashboard-02.moengage.com |
| DataCenter.DATA\_CENTER\_3 | dashboard-03.moengage.com |
| DataCenter.DATA\_CENTER\_4 | dashboard-04.moengage.com |
| DataCenter.DATA\_CENTER\_5 | dashboard-05.moengage.com |
| DataCenter.DATA\_CENTER\_6 | dashboard-06.moengage.com |
For more information about the detailed list of possible configurations, refer to the [API reference](https://moengage.github.io/android-api-reference/core/com.moengage.core/-mo-engage/-builder/index.html).
**Critical**
All the configurations are added to the builder before initialization. If you are calling initialize at multiple places, ensure that all the required flags and configurations are set each time you initialize to maintain consistency in behavior.
# Exclude MoEngage Storage File from Auto-Backup
This is a mandatory integration step and is very essential to prevent your data from getting corrupted. Android's auto back-up service periodically backs up Shared Preference files, Database files, etc, more details [here](/docs/developer-guide/android-sdk/sdk-integration/basic-integration/exclude-mo-engage-storage-file-from-auto-backup). This backup results in MoEngage SDK's identifiers being backed up and restored after re-install.This restoration of the identifier results in your data being corrupted and users not being reachable via push notifications.
To ensure data is not corrupted after a backup is restored, opt out of MoEngage SDK's storage files. Refer to [Exclude MoEngage Storage File from the Auto-Backup](/docs/developer-guide/android-sdk/sdk-integration/basic-integration/exclude-mo-engage-storage-file-from-auto-backup) section of the documentation to learn more about which files to exclude.
# Framework Initialization
Source: https://moengage.com/docs/developer-guide/flutter-sdk/sdk-integration/sdk-initialization/manual-initialization/framework-initialization
Initialize the MoEngage Flutter plugin in your app widget's initState using MoEngageFlutter.
# Initialize Plugin
Create **MoEngageFlutter(YOUR\_WORKSPACE\_ID)** object and in the project's App Widget call **initialise()** of **MoEngageFlutter** plugin in the **initState()** the method as shown below:
```Dart Dart theme={null}
import 'package:moengage_flutter/moengage_flutter.dart';
final MoEngageFlutter _moengagePlugin = MoEngageFlutter(YOUR_WORKSPACE_ID);
@override
void initState() {
super.initState();
initPlatformState();
_moengagePlugin.initialise();
}
```
For more information, refer to [Flutter SDK](https://github.com/moengage/Flutter-SDK).
Refer to the following for platform-specific initialization:
* [Android](/docs/developer-guide/flutter-sdk/sdk-integration/sdk-initialization/manual-initialization/android-sdk-initialization)
* [iOS](/docs/developer-guide/flutter-sdk/sdk-integration/sdk-initialization/manual-initialization/ios-sdk-initialization)
* [Web](/docs/developer-guide/flutter-sdk/sdk-integration/sdk-initialization/web-sdk-initialization)
# iOS SDK Initialization
Source: https://moengage.com/docs/developer-guide/flutter-sdk/sdk-integration/sdk-initialization/manual-initialization/ios-sdk-initialization
Initialize the MoEngage Flutter SDK in your iOS AppDelegate using the MoEngageInitializer instance.
# Initialization
To initialize the iOS Application with the MoEngage Workspace ID from Settings in the dashboard. In your project, go to the ***AppDelegate*** file and call either of the ***initialize()*** of **\_MoEngageInitializer \_**instance in ***applicationdidFinishLaunchingWithOptions()*** as shown below:
Make sure to set the correct Data Center while initializing the SDK. For more information, refer to the following [link](/docs/developer-guide/ios-sdk/sdk-integration/basic/data-center).
```swift Swift theme={null}
/// Method to initialize MoEngage SDK
/// - Parameters:
/// - config: MoEngageSDKConfig instance for SDK configuration
/// - launchOptions: Launch Options dictionary
func initializeDefaultInstance(_ config: MoEngageSDKConfig, launchOptions: [UIApplication.LaunchOptionsKey: Any]? = nil)
/// Method to initialize MoEngage SDK with SDK state
/// - Parameters:
/// - config: MoEngageSDKConfig instance for SDK configuration
/// - sdkState: Bool indicating if SDK is Enabled/Disabled
/// - launchOptions: Launch Options dictionary
func initializeDefaultInstance(_ config: MoEngageSDKConfig, sdkState: Bool = true, launchOptions: [UIApplication.LaunchOptionsKey: Any]? = nil)
```
Sample code to initialize in ***applicationdidFinishLaunchingWithOptions()***
```swift Swift theme={null}
// Import SDK frameworks
import moengage_flutter_ios
import MoEngageSDK
@UIApplicationMain
@objc class AppDelegate: FlutterAppDelegate {
override func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
//Workspace ID: You can be obtain it from App Settings in MoEngage Dashboard.
let sdkConfig = MoEngageSDKConfig(withAppID: YOUR_WORKSPACE_ID, dataCenter: DATA_CENTER)
sdkConfig.enableLogs = true
MoEngageInitializer.sharedInstance.initializeDefaultInstance(sdkConfig, launchOptions: launchOptions)
GeneratedPluginRegistrant.register(with: self)
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
}
}
```
```objectivec Objective-C theme={null}
// Import SDK frameworks
#import
#import
@implementation AppDelegate
- (BOOL)application:(UIApplication *)application
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
MoEngageSDKConfig* sdkConfig = [[MoEngageSDKConfig alloc] initWithAppID:@"Workspace ID"dataCenter: DATA_CENTER];
sdkConfig.enableLogs = true;
[[MoEngageInitializer sharedInstance] initializeDefaultInstance:sdkConfig launchOptions:launchOptions];
[GeneratedPluginRegistrant registerWithRegistry:self];
// Override point for customization after application launch.
return [super application:application didFinishLaunchingWithOptions:launchOptions];
}
@end
```
# Data Center
In case your app wants to redirect data to a specific zone due to any data regulation policy please configure the zone in the MOSDKConfig object.
For more information on Data Center, refer [here](/docs/developer-guide/ios-sdk/sdk-integration/basic/data-center).
# Web SDK Initialization
Source: https://moengage.com/docs/developer-guide/flutter-sdk/sdk-integration/sdk-initialization/web-sdk-initialization
Add the MoEngage Web SDK initialization script to your Flutter web application's index.html file.
Get the Workspace ID from the dashboard and replace "YOUR\_WORKSPACE\_ID" in the code below.
And get the Data Center according to your dashboard:
| Data Center | Dashboard host |
| ----------- | ------------------------- |
| dc\_1 | dashboard-01.moengage.com |
| dc\_2 | dashboard-02.moengage.com |
| dc\_3 | dashboard-03.moengage.com |
| dc\_4 | dashboard-04.moengage.com |
| dc\_6 | dashboard-06.moengage.com |
1. Add this initialization script to your `web/index.html` file:
```javascript JavaScript theme={null}
```
2. For web push integration refer this section of the Web Push guide.
# Android
Source: https://moengage.com/docs/developer-guide/flutter-sdk/sdk-integration/sdk-installation/android
Add the required Android dependencies and AndroidX libraries for the MoEngage Flutter SDK.
# Add dependencies
Add the following dependencies to the ***android/app/build.gradle*** file.
## Add Androidx Libraries
The SDK depends on a few Androidx libraries for its functioning, add the below Androidx libraries in your application if not done already.
```groovy build.gradle theme={null}
dependencies {
...
implementation("androidx.core:core:1.6.0")
implementation("androidx.appcompat:appcompat:1.3.1")
implementation("androidx.lifecycle:lifecycle-process:2.7.0")
}
```
The MoEngage SDK depends on the **lifecycle-process** library for a few key features to work and the latest version of **lifecycle-process** depends on the **androidx.startup:startup-runtime** library. Hence do not remove the **InitializationProvider** component from the manifest. When adding other Initializers using the **startup-runtime**, ensure the Initializer for the **lifecycle-process** library is also added. Refer to the [documentation](https://developer.android.com/jetpack/androidx/releases/lifecycle#2.4.0) to know how to add the Initializer.
# Framework Dependency
Source: https://moengage.com/docs/developer-guide/flutter-sdk/sdk-integration/sdk-installation/framework-dependency
Install the MoEngage Flutter SDK by adding moengage_flutter to your pubspec.yaml file.
Connect your IDE to the [MoEngage docs MCP server](/docs/developer-guide/connect-your-ide-to-moengage-docs) for accurate, context-aware SDK guidance inside your development environment. Your assistant searches this documentation directly, helping you move through the integration with confidence.
Flutter is Google’s UI toolkit for building natively compiled applications for iOS and Android from a single codebase.

# Plugin Installation
To add MoEngage's Flutter SDK to your application, edit your ***pubspec.yaml*** to add ***moengage\_flutter*** as a dependency.
```yaml pubspec.yaml theme={null}
dependencies:
moengage_flutter: $lastestVersion
```
***\$latestVersion*** refers to the latest version of the plugin.
Post including the dependency run the following command in terminal to install the dependency.
```yaml pubspec.yaml theme={null}
flutter pub get
```
A working Sample App can be found [here](https://github.com/moengage/Flutter-SDK).
After installing the plugin, use the following platform-specific configuration.
* [Android](/docs/developer-guide/flutter-sdk/sdk-integration/sdk-installation/android)
* [iOS](/docs/developer-guide/flutter-sdk/sdk-integration/sdk-installation/ios)
* [Web](/docs/developer-guide/flutter-sdk/sdk-integration/sdk-initialization/web-sdk-initialization)
# iOS
Source: https://moengage.com/docs/developer-guide/flutter-sdk/sdk-integration/sdk-installation/ios
Set up the MoEngage iOS SDK dependency in your Flutter project using CocoaPods or Swift Package Manager.
We have added our native SDK `MoEngage-iOS-SDK` as a dependency for `moengage_flutter` plugin, hence run `flutter build ios` command once to generate the Pod file for your iOS project and run `pod install` inside your project's `ios` directory to add the plugin and native SDK to your iOS Project.
Support for [Swift Package Manager](https://docs.flutter.dev/packages-and-plugins/swift-package-manager/for-app-developers) is available from version 10.3.0.
# Troubleshooting and FAQs - Flutter
Source: https://moengage.com/docs/developer-guide/flutter-sdk/troubleshooting-and-faqs/troubleshooting-and-faqs-flutter
Find solutions to common MoEngage Flutter SDK issues with notifications, callbacks, and in-app messages.
# Android - Why are notifications not working in the background or killed state?
Ensure that you are initializing MoEngage in the Application class and in the main thread.
Sample code link - [GitHub](https://github.com/moengage/Flutter-SDK/blob/dc4cac19fe9fc04ca4d187265544a6a1e9c04c9e/example/android/app/src/main/java/com/moengage/sampleapp/SampleApplication.kt#L37)
# Android - Why are callbacks not working in the background or killed state?
MoEngage callbacks must be registered in your application's root/App widget, and after setting them up, you must call the MoEngage Plugin's initialize () method. Read more about [it here](/docs/developer-guide/flutter-sdk/push/basic/push-callback).
Sample code link - [GitHub](https://github.com/moengage/Flutter-SDK/blob/master/example/lib/main.dart)
# Android - Why are inapp/nudge deep links not working?
Unilke the push notification handling, MoEngage SDK doesn't handle inapp redirections by default (except for rich landing page), please refer to the [documentation here](/docs/developer-guide/flutter-sdk/in-app-messages/inapp-nativ). You must implement inapp click callback methods in your root/app widget and call moengage plugin initialise() method after registering for callbacks. In these callbacks, you must handle the redirection for deeplink or screen-based redirection. Callback documentation [is given here](/docs/developer-guide/flutter-sdk/in-app-messages/inapp-nativ).
# Android - Why are inapp callbacks not working?
Refer to [this documentation](/docs/developer-guide/flutter-sdk/in-app-messages/inapp-nativ) to set up in-app callbacks. Additionally, you must register the callbacks in your application's root/App widget, and after setting them up, you must call the MoEngage Plugin's initialize () method.
# Android - What is MoEDebuggerActivity?
The MoEngage SDK bundles the native MoEngage Android SDK, so your Android build includes `MoEDebuggerActivity`, a component that supports on-device SDK debugging. Refer to [What is MoEDebuggerActivity?](/docs/developer-guide/android-sdk/troubleshooting-and-faqs/troubleshooting-and-faqs) to understand what it does.
To remove it from your app, add the following to your Android project's `AndroidManifest.xml`:
```xml theme={null}
```
# SDK Integration Guide
Source: https://moengage.com/docs/developer-guide/introduction
Integrate MoEngage SDKs across mobile, web, and cross-platform frameworks for customer engagement.
The MoEngage SDK is a lightweight, modular client library that runs inside your application. It handles event tracking, user identification, push token registration, in-app message rendering, and content card delivery — syncing data to MoEngage servers via batched HTTPS requests with offline queuing.
**Out of the box, the SDK automatically collects:** session start/end, app version, device model, OS version, timezone, and locale. Push tokens are registered automatically when push is configured. All other tracking — events, user attributes — is explicit via your API calls.
## Supported Platforms
### Native
Kotlin and Java. Push (FCM, HMS), in-app messaging, cards, push templates, push amplification, notification center, location triggers, and Android TV support. Modular — add only what you need.
Swift and Objective-C. Push (APNs), in-app messages, cards, push templates, Live Activities, real-time triggers, notification center, and Apple TV support. Available via CocoaPods or SPM.
Framework-agnostic JavaScript SDK. Web push, on-site messaging (OSM), data tracking, cards, and lifecycle callbacks. Works with React, Angular, Vue, Next.js, and any SPA or MPA. Also supports AMP pages, browser extensions, WebView, and Smart TVs.
### Cross-Platform
Cross-platform SDKs provide a unified API that bridges to the native Android and iOS SDKs. You will need to complete platform-specific setup steps for push notifications, in-app rendering, and other native features alongside the framework-level integration.
Supports React Native CLI projects. Push, in-app, cards, and TV support. Android native setup required; iOS auto-links via CocoaPods.
Expo managed workflow support via **react-native-expo-moengage** config plugin. Push, in-app, and cards. Requires EAS Build for native module linking.
Dart plugin with native bindings for iOS, Android, and Web. Push, in-app, cards, geofence, and notification center. Null-safe (Dart 2.12+).
Apache Cordova plugin. Push, in-app, and data tracking. Requires **cordova-ios** 4.3+ for CocoaPods support.
Capacitor plugin (Capacitor 3+). Push, in-app, data tracking, and geofence. TypeScript API.
Ionic uses Cordova or Capacitor as its native runtime. Choose the plugin that matches your Ionic project's native layer — there is no separate Ionic-specific SDK.
Unity plugin with native bindings for iOS and Android. Push, in-app messaging, data tracking, push templates, and location-triggered campaigns.
### Specialty
Android TV (via Android SDK), Apple TV (via iOS SDK), and Smart TV (via Web SDK). Push and in-app support varies by platform.
Shopify 2.0 (app embed block), Magento, and WooCommerce. Auto-tracks ecommerce events and syncs product catalogs.
## Platform Compatibility
| Platform | Languages | Min Version | Package Manager | Install Guide |
| ---------------- | ---------------------- | ----------------------------------------------------------------------------------------------------- | --------------- | ------------------------------------------------------------------------------------------------------------------------- |
| **Android** | Kotlin, Java | API 23 (Android 6.0), compileSdk 36 | Maven Central | [BOM (recommended)](/docs/developer-guide/android-sdk/sdk-integration/basic-integration/Install-Using-BOM) |
| **iOS** | Swift, Objective-C | iOS 13, Xcode 15+ | CocoaPods, SPM | [SPM](/docs/developer-guide/ios-sdk/sdk-integration/basic/sdk-integration) |
| **Web** | JavaScript | [Browser matrix](/docs/developer-guide/web-sdk/web-sdk-overview/web-sdk-browser-compatibility-matrix) | CDN or npm | [Integration guide](/docs/developer-guide/web-sdk/web-sdk-integration/basic-integration/web-sdk-integration) |
| **React Native** | TypeScript/JS + native | RN 0.60+ (inherits native minimums) | npm | [RN CLI guide](/docs/developer-guide/react-native-sdk/sdk-integration/react-native/sdk-installation/framework-dependency) |
| **Expo** | TypeScript/JS + native | Expo SDK 47+ (inherits native minimums) | npm | [Expo plugin guide](/docs/developer-guide/react-native-sdk/sdk-integration/expo/installation) |
| **Flutter** | Dart + native | Dart 2.12+, Flutter 3.x (inherits native minimums) | pub.dev | [Flutter guide](/docs/developer-guide/flutter-sdk/sdk-integration/sdk-installation/framework-dependency) |
| **Cordova** | JavaScript + native | cordova-ios 4.3+, cordova-cli 6.4+ | npm | [Cordova guide](/docs/developer-guide/cordova-sdk/sdk-integration/sdk-installation/framework-dependency) |
| **Capacitor** | TypeScript + native | Capacitor 3+ (inherits native minimums) | npm | [Capacitor guide](/docs/developer-guide/capacitor-sdk/sdk-integration/basic/sdk-installation/framework-dependency) |
## Quick Start
Pick your platform and follow the integration guide. Estimated time to first event: **15–30 minutes** for native SDKs, **30–60 minutes** for cross-platform (due to additional native configuration).
In the MoEngage Dashboard, go to **Settings → Account → APIs → Workspace ID**. You'll need this to initialize the SDK.
Your data center determines the API endpoint the SDK communicates with. Find your data center from the dashboard URL host (e.g., `dashboard-01.moengage.com` → `dc_1`). This must be configured during SDK initialization.
Follow the platform-specific installation guide from the table above.
Call the initialization method with your Workspace ID and data center. Use the **TEST** environment during development and **LIVE** for production.
Call the event tracking API to send a custom event. Verify it appears in the MoEngage Dashboard under **Analytics → Event Analysis**.
### Prerequisites by platform
Before starting integration, ensure you have:
* **Android:** Firebase project with `google-services.json` configured (required for FCM push). Gradle 7.0+, AGP 7.0+, Java 8+.
* **iOS:** Apple Developer account with push notification entitlement enabled. APNs Authentication Key (`.p8` file) uploaded to MoEngage Dashboard.
* **Web:** HTTPS-enabled domain (required for web push via Service Workers).
* **Cross-Platform (React Native / Expo / Flutter / Cordova / Capacitor):** All of the above native prerequisites, plus the framework-specific toolchain.
## Feature Availability by Platform
Not every feature is available on every platform. Use this matrix to verify support before starting integration.
| Feature | Android | iOS | Web | React Native | Expo | Flutter | Cordova | Capacitor |
| -------------------------------------------- | :--------: | :----: | :--------: | :----------: | :--: | :-----: | :-----: | :-------: |
| **Push Notifications** | ✅ FCM, HMS | ✅ APNs | ✅ Web Push | ✅ | ✅ | ✅ | ✅ | ✅ |
| **Push Templates** | ✅ | ✅ | — | ✅ | ✅ | ✅ | ✅ | ✅ |
| **Push Amplification** | ✅ | — | — | ✅ | ✅ | ✅ | ✅ | ✅ |
| **Device Triggered Push** | ✅ | ✅ | — | ✅ | ✅ | ✅ | ✅ | ✅ |
| **Live Activities** | — | ✅ | — | ✅ | ✅ | ✅ | ✅ | ✅ |
| **HTML In-App Messages (Templates, Nudges)** | ✅ | ✅ | — | ✅ | ✅ | ✅ | ✅ | ✅ |
| **In-App NATIV** | ✅ | ✅ | — | ✅ | ✅ | ✅ | ✅ | ✅ |
| **On-Site Messaging (OSM)** | — | — | ✅ | — | — | — | — | — |
| **Cards** ¹ | ✅ | ✅ | ✅ | ✅ ² | ✅ ² | ✅ ² | — | — |
| **Data Tracking** | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
| **Location/Geofence** | ✅ | ✅ | — | ✅ | ✅ | ✅ | ✅ | ✅ |
| **Notification Center (Inbox)** | ✅ | ✅ | — | ✅ | ✅ | ✅ | — | — |
| **Web Personalization** | — | — | ✅ | — | — | — | — | — |
| **GDPR Opt-outs** | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
| **Personalize** | ✅ | ✅ | ✅ | ✅ | — | ✅ | — | — |
¹ Android, iOS, Web, and both UI Cards and Self-Handled Cards.
² Self-handled only.
Some features require additional module dependencies. Check the platform-specific integration guide for exact dependency requirements.
## Common Integration Tasks
| Task | Android | iOS | Web |
| ------------------------ | ----------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------- |
| Install SDK | [Build settings](/docs/developer-guide/android-sdk/sdk-integration/basic-integration/configuring-build-settings) | [SDK Integration](/docs/developer-guide/ios-sdk/sdk-integration/basic/sdk-integration) | [Web SDK Integration](/docs/developer-guide/web-sdk/web-sdk-integration/basic-integration/web-sdk-integration) |
| Initialize | [Initialization](/docs/developer-guide/android-sdk/sdk-integration/basic-integration/sdk-initialization) | [Initialization](/docs/developer-guide/ios-sdk/sdk-integration/basic/sdk-initialization) | [Integration](/docs/developer-guide/web-sdk/web-sdk-integration/basic-integration/web-sdk-integration) |
| Set up push | [Push Config](/docs/developer-guide/android-sdk/push/basic/push-configuration) | [Push Integration](/docs/developer-guide/ios-sdk/push/basic/ios-push-integration-tutorial) | [Web Push](/docs/developer-guide/web-sdk/web-push/configure-and-integrate-web-push) |
| Track events | [Track Events](/docs/developer-guide/android-sdk/data-tracking/basic/track-events) | [Track Events](/docs/developer-guide/ios-sdk/data-tracking/basic/tracking-events) | [Events Tracking](/docs/developer-guide/web-sdk/data-tracking/web-sdk-events-tracking) |
| Track user attributes | [User Attributes](/docs/developer-guide/android-sdk/data-tracking/basic/track-user-attributes) | [User Attributes](/docs/developer-guide/ios-sdk/data-tracking/basic/tracking-user-attributes) | [User Attributes](/docs/developer-guide/web-sdk/data-tracking/web-sdk-user-attributes-tracking) |
| Enable in-app / OSM | [In-App NATIV](/docs/developer-guide/android-sdk/in-app-messages/in-app-nativ) | [In-App NATIV](/docs/developer-guide/ios-sdk/in-app-messages/in-app-nativ) | [On-Site Messaging](/docs/developer-guide/web-sdk/onsite-messaging/configure-and-integrate-on-site-messaging) |
| Add cards | [Cards](/docs/developer-guide/android-sdk/cards/cards) | [Cards](/docs/developer-guide/ios-sdk/cards/cards-in-i-os) | [Cards](/docs/developer-guide/web-sdk/cards/cards) |
| Compliance | [Compliance](/docs/developer-guide/android-sdk/compliance/compliance) | [Compliance](/docs/developer-guide/ios-sdk/compliance/compliance) | [Data opt-out](/docs/developer-guide/web-sdk/data-tracking/configure-data-opt-out-in-web-sdk) |
| Validate SDK integration | [Integration Validation](/docs/user-guide/getting-started/integration-validation/android-native-integration-validation) | [Release Checklist](/docs/developer-guide/ios-sdk/checklist/release-checklist) | [MoEngage Assist Chrome Extension](/docs/developer-guide/web-sdk/integration-validation/moengage-assist-chrome-extension) |
## Upgrading & Migration
[10.x → 11.x](/docs/developer-guide/android-sdk/migration/updating-to-11xxx-from-10xxx) · [11.x → 12.x](/docs/developer-guide/android-sdk/migration/updating-to-12xxx-from-11xxx) · [Manifest → Code-based init](/docs/developer-guide/android-sdk/migration/moving-from-manifest-to-code-based-integration) · [Maven Central migration](/docs/developer-guide/android-sdk/migration/migration-to-maven-central)
[→ v6.0.0](/docs/developer-guide/ios-sdk/migration/migration-to-sdk-version-6-0-0) · [→ v7.0.0](/docs/developer-guide/ios-sdk/migration/migration-to-sdk-version-7-0-0) · [→ v8.2.0](/docs/developer-guide/ios-sdk/migration/migration-to-sdk-version-8-2-0) · [→ v9.0.0](/docs/developer-guide/ios-sdk/migration/migration-to-sdk-version-9-0-0)
## Developer Resources
Working reference implementations: [Android](/docs/developer-guide/android-sdk/sample-app/android-sample-app) · [iOS](/docs/developer-guide/ios-sdk/sample-app/i-os-sample-app) · [React Native](/docs/developer-guide/react-native-sdk/sample-app/react-native-sample-app) · [Flutter](/docs/developer-guide/flutter-sdk/sample-app/flutter-sample-app) · [Capacitor](/docs/developer-guide/capacitor-sdk/sample-app/capacitor-sample-app)
Changelogs for every SDK. [Subscribe to releases](/docs/release-notes/sdks/ios) via GitHub to get notified of new versions, breaking changes, and security patches.
[Android](/docs/developer-guide/android-sdk/troubleshooting-and-faqs/troubleshooting-and-faqs) · [iOS](/docs/developer-guide/ios-sdk/troubleshooting-and-faqs/troubleshooting-and-faqs-ios) · [React Native](/docs/developer-guide/react-native-sdk/troubleshooting-and-faqs/troubleshooting-and-faqs-react) · [Flutter](/docs/developer-guide/flutter-sdk/troubleshooting-and-faqs/troubleshooting-and-faqs-flutter) · [Capacitor](/docs/developer-guide/capacitor-sdk/troubleshooting-and-faqs/troubleshooting-and-faqs-capacitor)
Pre-launch verification: [Android](/docs/developer-guide/android-sdk/checklist/release-checklist) · [iOS](/docs/developer-guide/ios-sdk/checklist/release-checklist)
[Android size](/docs/developer-guide/android-sdk/performance/sdk-size-impact) · [Android performance](/docs/developer-guide/android-sdk/performance/sdk-performance) · [iOS framework size](/docs/developer-guide/ios-sdk/framework-size-impact/framework-size-impact)
[Android compliance](/docs/developer-guide/android-sdk/compliance/compliance) · [iOS compliance](/docs/developer-guide/ios-sdk/compliance/compliance) · [Google Play data disclosure](/docs/developer-guide/android-sdk/compliance/prepare-for-google-plays-data-disclosure-requirements) · [GDPR/CCPA API](/docs/api/gdpr-ccpa/gdpr-ccpa-overview)
Need help? Contact your Customer Success Manager.
# SDK Installation
Source: https://moengage.com/docs/developer-guide/ionic-sdk/sdk-integration/sdk-installation
Add the cordova-moengage-core plugin to your Ionic project and configure your workspace ID.
Connect your IDE to the [MoEngage docs MCP server](/docs/developer-guide/connect-your-ide-to-moengage-docs) for accurate, context-aware SDK guidance inside your development environment. Your assistant searches this documentation directly, helping you move through the integration with confidence.
# Adding MoEngage Plugin
Add `cordova-moengage-core` plugin to Ionic project as shown below :
```Shell Shell theme={null}
$ ionic cordova plugin add cordova-moengage-core --variable APP_ID="[YOUR_WORKSPACE_ID]"
```
# Variables
* **APP\_ID** = Workspace id found under the settings page on the MoEngage dashboard.
# Plugin Name Update
Starting from version 5.0.2, we have changed the name of our plugin. Earlier it was registered as `moengagesdk` and now it is renamed to `cordova-moengage-core`. In case if you are updating from version 5.0.1 or earlier. Please make sure to remove the older version of the plugin first and then install the newer plugin as shown below:
```Shell Shell theme={null}
$ ionic cordova plugin rm cordova-plugin-moengage
$ ionic cordova plugin add cordova-moengage-core --variable APP_ID="[YOUR_WORKSPACE_ID]"
```
# MoEngage Declaration
Make sure you add the below line to your typescript files before calling any MoEngage SDK API.
```TypeScript TypeScript theme={null}
declare var MoECordova: any;
```
**Refer Cordova Docs**
As you can see the plugin used here for Ionic is the same which is built by us for Cordova framework, therefore please follow the Cordova SDK Documentation starting from the [SDK Installation](/docs/developer-guide/cordova-sdk/sdk-integration/sdk-installation/framework-dependency) for integrating the plugin to your project and using the different feature provided in the plugin.
# Apple TV
Source: https://moengage.com/docs/developer-guide/ios-sdk/apple-tv/apple-tv
Learn about MoEngage SDK support for Apple TV including data tracking, in-app, and cards features.
MoEngage supports your apps available on Apple TV.
**Prerequisites**
Ensure that the [iOS SDK integration](/docs/developer-guide/ios-sdk/sdk-integration/basic/sdk-integration) is completed.
# Supported Features
| Feature | SDK version | Module Version |
| :------------------------------------------------------------------------------------------ | :---------- | :--------------------- |
| [Data tracking](/docs/developer-guide/ios-sdk/data-tracking/basic/tracking-user-attributes) | 8.2.0 | |
| [Self-handled InApp](/docs/developer-guide/ios-sdk/in-app-messages/in-app-nativ) | 9.13.0 | MoEngageInApp - 4.12.1 |
| [Self-handled Cards](/docs/developer-guide/ios-sdk/cards/self-handled-cards) | 9.13.0 | MoEngageCards - 4.12.1 |
# Cards in iOS
Source: https://moengage.com/docs/developer-guide/ios-sdk/cards/cards-in-i-os
Create and display card campaigns in your iOS app using the MoEngage Cards SDK via SPM or CocoaPods.
Create targeted or automated App Inbox/NewsFeed messages that can be grouped into various categories, and target your users with different updates or offers that can stay in the Inbox/Feed over a designated period of time. Refer to the [help article](https://www.moengage.com/docs/user-guide/campaigns-and-channels/cards/create/create-a-card-campaign) to learn more about cards.
# SDK Installation
## Install using Swift Package Manager
MoEngageCards is supported through SPM from SDK version 3.2.0. To integrate use the following GitHub URL link and set the branch as master or version as 4.11.1 and above [https://github.com/moengage/MoEngage-iOS-Cards.git](https://github.com/moengage/MoEngage-iOS-Cards.git)
## Install using CocoaPod
**Information**
CocoaPods is being deprecated. MoEngage recommends using Swift Package Manager for all new integrations. For detailed info on cocoapods, refer to [CocoaPods Integration Guide](https://www.moengage.com/docs/developer-guide/ios-sdk/sdk-integration/basic/integration-through-cocoa-pods).
Integrate the MoEngageCards framework by adding the dependency in the pod file as described in the following image.
```ruby Ruby wrap theme={null}
pod 'MoEngage-iOS-SDK/Cards',
```
Now run `pod install` to install the framework
## Manual Integration
**Manual Integration**
To integrate the `MoEngageCards` SDK manually to your project follow this [doc](https://www.moengage.com/docs/developer-guide/ios-sdk/manual-integration/manual-integration).
# Displaying AppInbox/Feeds
Once the module is integrated, use the below-provided methods to display the `MoEngageCardsListViewController` with the transition:
```swift Swift lines wrap theme={null}
// To Push MoEngageCardsListViewController
MoEngageSDKCards.sharedInstance.pushCardsViewController(toNavigationController: self.navigationController!)
// To Present MoEngageCardsListViewController
MoEngageSDKCards.sharedInstance.presentCardsViewController()
```
```objective-c objective-c theme={null}
// To Push MoEngageCardsListViewController
[[MoEngageSDKCards sharedInstance] pushCardsViewControllerToNavigationController:navigationController withUIConfiguration:nil withCardsViewControllerDelegate:nil forAppID:@"YOUR_WORKSPACE_ID"];
// To Present MoEngageCardsListViewController
[[MoEngageSDKCards sharedInstance] presentCardsViewControllerWithUIConfiguration:nil withCardsViewControllerDelegate:nil forAppID:@"YOUR_WORKSPACE_ID"];
```
Starting with iOS SDK [11.0.0](/docs/release-notes/sdks/ios#22nd-july-2026), `presentCardsViewController` and `pushCardsViewController` carry `@MainActor` annotation. Objective-C callers remain unaffected. Swift code inside a `UIViewController` (as shown above) already runs on the main actor, so these calls compile unchanged. When called from a non-main-actor Swift context, use `await MainActor.run { ... }` to transition to the main actor.
So as shown above, in the SDK we have provided support for Push and Present transition. In case you want to handle the transition while displaying the Inbox, use the [*getCardsViewController(withUIConfiguration:withCardsViewControllerDelegate:forAppID:withCompletionBlock:)*](https://moengage.github.io/ios-api-reference/Classes/MoEngageSDKCards.html#/c:@M@MoEngageCards@objc\(cs\)MoEngageSDKCards\(im\)getCardsViewControllerWithUIConfiguration:withCardsViewControllerDelegate:forAppID:withCompletionBlock:) method as shown below to obtain the view controller instance:
```swift Swift wrap theme={null}
MoEngageSDKCards.sharedInstance.getCardsViewController(withUIConfiguration: nil, withCardsViewControllerDelegate: nil, forAppID: "YOUR_WORKSPACE_ID") { cardsController in
print("fetched CardsController")
}
```
```objective-c Objective C wrap theme={null}
[[MoEngageSDKCards sharedInstance] getCardsViewControllerWithUIConfiguration:nil withCardsViewControllerDelegate:nil forAppID:@"YOUR_WORKSPACE_ID" withCompletionBlock:^(MoEngageCardsListViewController * _Nullable) {
self.cardsController = cardsController;
}];
```
Starting with iOS SDK [11.0.0](/docs/release-notes/sdks/ios#22nd-july-2026), `getCardsViewController` carries `@MainActor` annotation. Objective-C callers remain unaffected. Swift code inside a `UIViewController` (as shown above) already runs on the main actor, so this call compiles unchanged. When called from a non-main-actor Swift context, use `await MainActor.run { ... }` to transition to the main actor.
# Customizing Inbox UI
The earlier snapshots indicate what the default UI of the Inbox would look like. But we have also added support for customizing the App Inbox screen according to your App Theme. For customizing the screen make use of [*MoEngageCardsUIConfiguration*](https://moengage.github.io/ios-api-reference/Classes/MoEngageCardsUIConfiguration.html) instance and pass the same in the above-mentioned methods. Refer to the example below:
```swift Swift wrap theme={null}
let uiConfig = MoEngageCardsUIConfiguration()
// Do customization using uiConfig
// provide the argument while obtaining the MoEngageCardsListViewController instance
// Present Cards View Controller
MoEngageSDKCards.sharedInstance.presentCardsViewController(withUIConfiguration: uiConfig)
// Push Cards View Controller
MoEngageSDKCards.sharedInstance.pushCardsViewController(toNavigationController: self.navigationController!, withUIConfiguration: uiConfig)
// Obtaining the ViewController
MoEngageSDKCards.sharedInstance.getCardsViewController(withUIConfiguration: uiConfig, withCardsViewControllerDelegate: uiConfig, forAppID: "YOUR_WORKSPACE_ID") { cardsController in
self.cardsController = cardsController
}
```
The example of how the UI of Inbox can be completely customized according to your need. Below we have mentioned about what all UI attributes which can be customized using [*MoEngageCardsUIConfiguration*](https://moengage.github.io/ios-api-reference/Classes/MoEngageCardsUIConfiguration.html) instance:
## Customizing Navigation Bar:
Navigation Bar customization includes updating the title, navigation bar color, title color, title font, etc. Create an instance of [*MoEngageCardsNavigationBarStyle*](https://moengage.github.io/ios-api-reference/Classes/MoEngageCardsNavigationBarStyle.html) and set all the attributes as shown below, post that assign the same to your [*MoEngageCardsUIConfiguration*](https://moengage.github.io/ios-api-reference/Classes/MoEngageCardsUIConfiguration.html) instance:
```swift Swift wrap theme={null}
let uiConfig = MoEngageCardsUIConfiguration()
// Navigation Bar Customizations
let navBarStyle = MoEngageCardsNavigationBarStyle()
navBarStyle.navigationBarColor = UIColor(hex: "#0A1D1F")
navBarStyle.navigationBarTitleFont = UIFont.systemFont(ofSize: 20.0, weight: .semibold)
navBarStyle.navigationBarTitleColor = UIColor(hex: "#FFFFFF")
navBarStyle.navigationBarTintColor = UIColor(hex: "#FFFFFF")
navBarStyle.navigationBarTransluscent = true
uiConfig.navigationBarTitle = "Hello!!!"
uiConfig.navigationBarStyle = navBarStyle
```
## Customizing Category TabBar:
Directly set the attributes of [*MoEngageCardsUIConfiguration*](https://moengage.github.io/ios-api-reference/Classes/MoEngageCardsUIConfiguration.html) instance, you would like to change for the Category Tabs Bar view as shown below:
```swift Swift wrap theme={null}
let uiConfig = MoEngageCardsUIConfiguration()
// Category Tabs Customizations
uiConfig.categoryTabsContainerBGColor = UIColor(hex: "#6EA6CF")
uiConfig.categoryTabsBGColor = UIColor(hex: "#6EA6CF")
uiConfig.categoryTabsTextColor = UIColor(hex: "#0A1D1F")
uiConfig.categorySelectedTabBGColor = UIColor(hex: "#BB4D3E")
uiConfig.categorySelectedTabTextColor = UIColor(hex: "#FFFFFF")
uiConfig.categorySelectionIndicatorBarColor = UIColor(hex: "#B0BF40")
uiConfig.categoryTabFont = UIFont.systemFont(ofSize: 12.0, weight: .medium)
uiConfig.categorySelectedTabFont = UIFont.systemFont(ofSize: 16.0, weight: .bold)
```
## Customizing Empty Inbox:
In the case of an empty inbox, we provide the option of setting a message and image. By default, the empty inbox will look as described in the following image:
This can again be customized by using the UI configuration instance as shown below:
```swift Swift wrap theme={null}
let uiConfig = MoEngageCardsUIConfiguration()
// Empty Inbox Customization
uiConfig.emptyInboxText = "No New Messages!!!"
uiConfig.emptyInboxTextColor = UIColor(hex:"#BB4D3E") ?? .white
uiConfig.emptyInboxTextFont = UIFont.systemFont(ofSize: 28.0, weight: .bold)
uiConfig.emptyInboxImage = UIImage(named: "emptyInbox")
```
## Customising Inbox Container:
In case any of the property of `MoEngageCardsListViewController` has to be customized, refer to the below example:
```swift Swift wrap theme={null}
let uiConfig = MoEngageCardsUIConfiguration()
// Cards Container customization
uiConfig.cardsViewControllerBGColor = UIColor(hex:"#0A1D1F")
uiConfig.cardsTableViewBGColor = UIColor(hex:"#0A1D1F")
uiConfig.pullToRefreshTintColor = UIColor(hex:"#BB4D3E")
// To disable Pull to refresh set it to false, set to true by default
uiConfig.enablePullToRefresh = false
// To change the Delete/Cancel text in Action Sheet
uiConfig.actionSheetDeletionText = "Remove"
uiConfig.actionSheetCancelText = "Never Mind" // New Updates Button Customizations
uiConfig.newUpdatesButtonTitle = "Updates Available!!"
uiConfig.newUpdatesButtonFont = UIFont.systemFont(ofSize: 14.0, weight: .semibold)
uiConfig.newUpdatesButtonBGColor = UIColor(hex:"#BB4D3E")
uiConfig.newUpdatesButtonTextColor = UIColor(hex: "#FFFFFF")
```
**Note**
We have supported pull to refresh in the Inbox, the activity indicator color for the same can be updated as shown above.
## Customizing Card Properties:
We have provided options to customize your Card in the dashboard while creating the campaign, but along with it you can also set the default attribute values so that you don't have to set it every time while creating the campaign, refer to the example below:
```swift Swift wrap theme={null}
let uiConfig = MoEngageCardsUIConfiguration()
// Cards Default parameters
uiConfig.defaultCardBackgroundColor = UIColor(hex:"#6EA6CF")
// On highlighting the cell
uiConfig.cardSelectionTintColor = UIColor(hex:"#333333")
// Header Label textcolor and font
uiConfig.cardHeaderLabelFont = UIFont.init(name: "AmericanTypewriter", size: 20.0)!
uiConfig.cardHeaderLabelDefaultTextColor = UIColor(hex:"#0A1D1F")
// Message Label textcolor and font
uiConfig.cardMessageLabelFont = UIFont.init(name: "Baskerville", size: 16.0)!
uiConfig.cardMessageLabelDefaultTextColor = UIColor(hex:"#0F2E2A")
// TimeStamp Date Format, Label textcolor and font
uiConfig.timestampDateFormat = "dd/MM, HH:mm"
uiConfig.cardTimestampLabelFont = UIFont.init(name: "Courier", size: 14.0)!
uiConfig.cardTimestampLabelDefaultTextColor = UIColor(hex:"#0F2E2A")
// CTA Button customizations
uiConfig.cardButtonFont = UIFont.init(name: "SavoyeLetPlain", size: 16.0)!
uiConfig.cardButtonDefaultTextColor = UIColor(hex:"#FFFFFF")
uiConfig.cardButtonDefaultBGColor = UIColor(hex:"#BB4D3E")
// Image Customizations
uiConfig.cardImageBackgroundColor = UIColor.clear
uiConfig.cardPlaceholderImage = UIImage(named: "card-placeholder")
// Card Pinned Indicator
uiConfig.cardPinnedImage = UIImage(named: "pinned")
// Unclicked Indicator
// Either Color OR Image NOT Both, If both are set then image will be considered
uiConfig.cardUnclickedIndicatorColor = UIColor(hex:"#7EC247")
uiConfig.cardUnclickedIndicatorImage = UIImage(named: "unclicked")
// Separator
uiConfig.cardSeparatorBackgroundColor = UIColor(red: 10.0/255.0, green: 90.0/255.0, blue: 190.0/255.0, alpha: 0.30)
```
# Getting Cards Count APIs
## Getting New Cards Count:
A Card is considered new if it's not yet seen by the user. To get the number/count of new cards use the [*getNewCardsCount(forAppID:withCompletionBlock:)*](https://moengage.github.io/ios-api-reference/Classes/MoEngageSDKCards.html#/c:@M@MoEngageCards@objc\(cs\)MoEngageSDKCards\(im\)getNewCardsCountForAppID:withCompletionBlock:) method as shown below:
```swift Swift wrap theme={null}
MoEngageSDKCards.sharedInstance.getNewCardsCount(forAppID: "YOUR_WORKSPACE_ID") { count, accountMeta in
print("Card count is \(count)")
})
```
## Getting Unclicked Cards Count:
To get the number/count of cards which are not clicked by the user, use the [*getUnclickedCardsCount(forAppID:withCompetionBlock:)*](https://moengage.github.io/ios-api-reference/Classes/MoEngageSDKCards.html#/c:@M@MoEngageCards@objc\(cs\)MoEngageSDKCards\(im\)getUnclickedCardsCountForAppID:withCompletionBlock:) method as shown below:
```swift Swift wrap theme={null}
MoEngageSDKCards.sharedInstance.getUnclickedCardsCount(forAppID: "YOUR_WORKSPACE_ID") { count, accountMeta in
print("UnClicked Card count is \(count)")
}
```
# Callbacks using MoEngageCardsDelegate
Use [*MoEngageCardsDelegate*](https://moengage.github.io/ios-api-reference/Protocols/MoEngageCardsDelegate.html) protocol for getting the callbacks from the Cards Module:
```swift Swift wrap theme={null}
@objc public protocol MoEngageCardsDelegate {
// Called when the Cards data is synced successfully
@objc optional func cardsSyncedSuccessfully(forAccountMeta accountMeta: MoEngageAccountMeta)
}
```
Set [*setCardsDelegate(delegate:forAppID:)*](https://moengage.github.io/ios-api-reference/Classes/MoEngageSDKCards.html#/c:@M@MoEngageCards@objc\(cs\)MoEngageSDKCards\(im\)setCardsDelegateWithDelegate:forAppID:) property of [*MoEngageSDKCards*](https://moengage.github.io/ios-api-reference/Classes/MoEngageSDKCards.html) instance as shown below to get the above callbacks:
```swift Swift wrap theme={null}
class DelegateClass: MoEngageCardsDelegate {
// ...
MoEngageSDKCards.sharedInstance.setCardsDelegate(delegate: self)// Pass delegate instance
```
# Callbacks using MoEngageCardsViewControllerDelegate
Use `MoEngageCardsViewControllerDelegate` protocol for getting the callbacks from the Cards Module:
```swift Swift wrap theme={null}
@objc public protocol MoEngageCardsViewControllerDelegate {
// Called when MoEngageCardsListViewController is dismissed after being presented
@objc optional func cardsViewControllerDismissed(forAccountMeta accountMeta: MoEngageAccountMeta)
// Called when a Card is deleted
@objc optional func cardDeleted(withCardInfo card: MoEngageCardCampaign, forAccountMeta accountMeta: MoEngageAccountMeta)
// Called when a Card is clicked by the user
@objc optional func cardClicked(withCardInfo card: MoEngageCardCampaign, andAction action:MoEngageCardAction, forAccountMeta accountMeta: MoEngageAccountMeta) -> Bool
}
```
Set `MoEngageCardsViewControllerDelegate` by passing the delegate as parameter in the below functions:
```swift Swift wrap theme={null}
class DelegateClass: MoEngageCardsViewControllerDelegate {
// ...
//Pass delegate instance when presenting the controller
MoEngageSDKCards.sharedInstance.presentCardsViewController(withUIConfiguration: nil, withCardsViewControllerDelegate: self)
//Pass delegate instance when pushing the controller
MoEngageSDKCards.sharedInstance.pushCardsViewController(toNavigationController: self.navigationController!, withUIConfiguration: nil, withCardsViewControllerDelegate: self)
//Pass delegate instance when fetching the controller
MoEngageSDKCards.sharedInstance.getCardsViewController(withUIConfiguration: uiConfig, withCardsViewControllerDelegate: uiConfig, forAppID: "YOUR_WORKSPACE_ID") { cardsController in
}
```
# Self Handled Cards
Source: https://moengage.com/docs/developer-guide/ios-sdk/cards/self-handled-cards
Build custom card views in your iOS app using the MoEngage self-handled cards SDK and APIs.
Self-handled cards give you the flexibility to create card campaigns on the MoEngage Platform and display the cards anywhere within the application. The SDK provides APIs to fetch the campaign's data, which allows you to create your own custom view for the cards.
# SDK Installation
## Install using Swift Package Manager
MoEngageCards is supported through SPM from SDK version 3.2.0. To integrate use the following git hub url link and set the branch as master or version as 4.0.0 and above [https://github.com/moengage/MoEngage-iOS-Cards.git](https://github.com/moengage/MoEngage-iOS-Cards.git)
## Install using CocoaPod
**Information**
CocoaPods is being deprecated. MoEngage recommends using Swift Package Manager for all new integrations. For detailed info on cocoapods, refer to [CocoaPods Integration Guide](/docs/developer-guide/ios-sdk/sdk-integration/basic/integration-through-cocoa-pods).
Integrate the MoEngageCards framework by adding the dependency in the podfile as shown below.
```ruby Ruby theme={null}
pod 'MoEngage-iOS-SDK/Cards',
```
Now run `pod install` to install the framework.
## Manual Integration
**Manual Integration**
To integrate the `MoEngageCards` SDK manually to your project follow this [doc](/docs/developer-guide/ios-sdk/manual-integration/manual-integration).
## Notify on Section Load
You can show the cards on a separate screen or a section of the screen. When the cards screen/section is loaded call [*onCardSectionLoaded()*](https://moengage.github.io/ios-api-reference/Classes/MoEngageSDKCards.html#/c:@M@MoEngageCards@objc\(cs\)MoEngageSDKCards\(im\)onCardSectionLoadedForAppID:withCompletion)
```swift Swift wrap theme={null}
MoEngageSDKCards.sharedInstance.onCardSectionLoaded(forAppID: "YOUR_WORKSPACE_ID") { data in
print("Card section loaded, hasUpdates: \(data?.hasUpdates ?? false)")
}
```
```objective-c Objective C wrap theme={null}
[[MoEngageSDKCards sharedInstance] onCardSectionLoadedForAppID:@"YOUR_WORKSPACE_ID"
withCompletion:^(MoEngageCardSyncCompleteData * _Nullable data) {
NSLog(@"Card section loaded, hasUpdates: %d", data.hasUpdates);
}];
```
Use the below APIs to fetch the card's data and build your own UI.
## Fetch Categories
To fetch all the categories for which cards are configured use the API [*getCardsCategories(forAppID:withCompletionBlock:)*](https://moengage.github.io/ios-api-reference/Classes/MoEngageSDKCards.html#/c:@M@MoEngageCards@objc\(cs\)MoEngageSDKCards\(im\)getCardsCategoriesForAppID:withCompletionBlock:)
```swift Swift wrap theme={null}
MoEngageSDKCards.sharedInstance.getCardsCategories { categories, accountMeta in
print("Fetched Cards Categories \(categories)")
}
```
```objective-c Objective C wrap theme={null}
[[MoEngageSDKCards sharedInstance] getCardsCategoriesForAppID:@"YOUR_WORKSPACE_ID" withCompletionBlock:^(NSArray * _Nonnull, MoEngageAccountMeta * _Nullable) {
NSLog(@"Fetched Cards Categories");
}];
```
## Fetch Cards for Categories
To fetch cards eligible for display for a specific category use the API [*getCards(forCategory:forAppID:withCompletionBlock:)*](https://moengage.github.io/ios-api-reference/Classes/MoEngageSDKCards.html#/c:@M@MoEngageCards@objc\(cs\)MoEngageSDKCards\(im\)getCardsForCategory:forAppID:withCompletionBlock:)
```swift Swift wrap theme={null}
MoEngageSDKCards.sharedInstance.getCards(forCategory: "CATEGORY") { cards, accountMeta in
print("Fetched cards for given category")
}
```
```objective-c Objective C wrap theme={null}
[[MoEngageSDKCards sharedInstance] getCardsForCategory:@"CATEGORY" forAppID:@"YOUR_WORKSPACE_ID" withCompletionBlock:^(NSArray * _Nonnull, MoEngageAccountMeta * _Nullable) {
NSLog(@"Fetched cards for given category");
}];
```
Instead of using separate APIs to fetch the Cards and categories you can use the method [*getCardsData(forAppID:withCompletionBlock:)*](https://moengage.github.io/ios-api-reference/Classes/MoEngageSDKCards.html#/c:@M@MoEngageCards@objc\(cs\)MoEngageSDKCards\(im\)getCardsDataForAppID:withCompletionBlock:) to fetch all the information in one go.
```swift Swift wrap theme={null}
MoEngageSDKCards.sharedInstance.getCardsData { cardsData, accountMeta in
print("Cards category \(cardsData?.cardCategories)")
print("Cards Data \(cardsData?.cards)")
}
```
```objective-c Objective C wrap theme={null}
[[MoEngageSDKCards sharedInstance] getCardsDataForAppID:@"YOUR_WORKSPACE_ID" withCompletionBlock:^(MoEngageCardsData * _Nullable, MoEngageAccountMeta * _Nullable) {
NSLog(@"Cards category %@", cardsData.cardCategories);
NSLog(@"Cards Data %@", cardsData.cards);
}];
```
## Track Statistics for Cards
Since the UI/display of the cards is controlled by the application to track statistics on delivery, display, click we need the application to notify the SDK.
### Delivered
To track delivery to the card section of the application use the API [*cardDelivered(\_:forAppID:)*](https://moengage.github.io/ios-api-reference/Classes/MoEngageSDKCards.html#/c:@M@MoEngageCards@objc\(cs\)MoEngageSDKCards\(im\)cardDelivered:forAppID:) when the cards section of the application is loaded by passing the instance of [*MoEngageCardCampaign*](https://moengage.github.io/ios-api-reference/Classes/MoEngageCardCampaign.html).
```swift Swift wrap theme={null}
MoEngageSDKCards.sharedInstance.cardDelivered(cardCampaign, forAppID: "YOUR_WORKSPACE_ID")
```
```objective-c Objective C wrap theme={null}
[[MoEngageSDKCards sharedInstance] cardDelivered:cardCamapigns forAppID:@"YOUR_WORKSPACE_ID"];
```
### Impression
Call the method [*cardShown(\_:forAppID:)*](https://moengage.github.io/ios-api-reference/Classes/MoEngageSDKCards.html#/c:@M@MoEngageCards@objc\(cs\)MoEngageSDKCards\(im\)cardShown:forAppID:) when a specific card is visible on the screen.
```swift Swift wrap theme={null}
MoEngageSDKCards.sharedInstance.cardShown(cardCampaign, forAppID: "YOUR_WORKSPACE_ID")
```
```objective-c Objective C wrap theme={null}
[[MoEngageSDKCards sharedInstance] cardShown:cardCamapign forAppID:@"YOUR_WORKSPACE_ID"];
```
### Click
Call the method [*cardClicked(\_:withWidgetID:forAppID:)*](https://moengage.github.io/ios-api-reference/Classes/MoEngageSDKCards.html#/c:@M@MoEngageCards@objc\(cs\)MoEngageSDKCards\(im\)cardClicked:withWidgetID:forAppID:) whenever a user clicks on a card, along with the card object widget identifier for the UI element clicked should also be passed.
```swift Swift wrap theme={null}
MoEngageSDKCards.sharedInstance.cardClicked(cardCampaign, withWidgetID: widgetID);
```
```objective-c Objective C wrap theme={null}
[[MoEngageSDKCards sharedInstance] cardClicked:cardCamapigns withWidgetID:widgetID forAppID:@"YOUR_WORKSPACE_ID"];
```
## Delete Card
Call the method [deleteCards(\_:forAppID:andCompletionBlock:)](https://moengage.github.io/ios-api-reference/Classes/MoEngageSDKCards.html#/c:@M@MoEngageCards@objc\(cs\)MoEngageSDKCards\(im\)deleteCards:forAppID:andCompletionBlock:) to delete a card by passing an array of [MoEngageCardCampaign](https://moengage.github.io/ios-api-reference/Classes/MoEngageCardCampaign.html) as parameter.
```swift Swift wrap theme={null}
MoEngageSDKCards.sharedInstance.deleteCards([cards]) { isDeleted, accountMeta in
print("Card deletion was \(isDeleted)")
}
```
```objective-c Objective C wrap theme={null}
[[MoEngageSDKCards sharedInstance] deleteCards:[cardCamapigns] forAppID:nil andCompletionBlock:^(BOOL isDeleted, MoEngageAccountMeta * _Nullable accountMeta) {
NSLog(@"Card deletion was %d", isDeleted);
}];
```
The above API has an overloaded method that accepts a list of cards to be deleted.
## Refresh Cards from the Server
Use the [*fetchCards(forAppID:withCompletion:)*](https://moengage.github.io/ios-api-reference/Classes/MoEngageSDKCards.html#/c:@M@MoEngageCards@objc\(cs\)MoEngageSDKCards\(im\)fetchCardsForAppID:withCompletion:) API to refresh cards from the MoEngage server if required, [*MoEngageCardData*](https://moengage.github.io/ios-api-reference/Classes/MoEngageCardData.html) is provided in callback with refreshed [*MoEngageCardCampaign*](https://moengage.github.io/ios-api-reference/Classes/MoEngageCardCampaign.html) in *cards* property and account meta-data [*MoEngageAccountMeta*](https://moengage.github.io/ios-api-reference/Classes/MoEngageAccountMeta.html) in *accountMeta*.
```swift Swift wrap theme={null}
MoEngageSDKCards.sharedInstance.fetchCards { data in
print("Refreshed cards: \(data?.cards) for account \(data?.accountMeta)")
}
```
```objective-c Objective C wrap theme={null}
[[MoEngageSDKCards sharedInstance] fetchCardsForAppID:nil withCompletion:^(MoEngageCardData * _Nullable data) {
NSLog(@"Refreshed cards: %@ for account %@", data.cards, data.accountMeta);
}];
```
**Note**
* The SDK automatically refreshes/fetches cards from the MoEngage server whenever the application comes to the foreground.
* This API has a FUP if breached the existing cards i.e. the ones in the local storage of the device will be passed on in the callback.
* For details on the sync timing and rate limits for `fetchCards()`, see [When Does the MoEngage SDK Sync Card Data?](/docs/user-guide/campaigns-and-channels/cards/faqs-cards/when-does-the-moengage-sdk-sync-card-data)
# Release Checklist
Source: https://moengage.com/docs/developer-guide/ios-sdk/checklist/release-checklist
Verify your MoEngage iOS SDK integration against this checklist before submitting to the App Store.
Before making the AppStore release, make sure you have verified with the release checklist below:
# 1. Update MoEngage SDK
* Update MoEngage SDK to the current version. Check [release notes](/docs/release-notes/sdks/ios) to know the latest SDK version.
# 2. Set Correct AppID
* In case using multiple MoEngage apps, then make sure before releasing that the correct AppID is set while initializing the SDK.
* Make sure that the initializeProdWithApiKey method is called for the build, which is being submitted to AppStore; this is to make sure that the data is tracked in the Live environment of the MoEngage app. Refer to the [link](/docs/developer-guide/ios-sdk/sdk-integration/basic/sdk-initialization) to know more.
# 3. Test Push Notifications and Verify APNS Certificate
* Make sure that the Production APNS certificate is uploaded to MoEngage dashboard settings in Live environment. And also, test the same by sending a few notifications before going live.
# 4. Tracking App Installs and Updates
* If you wish to run Install/Update campaigns, make sure you track the [same](/docs/developer-guide/ios-sdk/data-tracking/basic/install-update-differentiation) (recommended but optional).
# 5. User Attribute Unique ID
* If the app has a Login feature, then make sure you are tracking the USER\_ATTRIBUTE\_UNIQUE\_ID attribute when the user logs in. Setting USER\_ATTRIBUTE\_UNIQUE\_ID is mandatory to tie a user across devices, installs/uninstalls, and across different platforms. Refer to this [link](/docs/developer-guide/ios-sdk/data-tracking/basic/tracking-user-attributes).
* Also, make sure that the resetUser method is called on user logout. Refer to this [link](/docs/developer-guide/ios-sdk/data-tracking/basic/tracking-user-attributes).
# 6. Setting User Attributes
* Make sure you are setting all the User Attributes required to target users based on these attributes across devices or installs. Also, this will help in the personalization of campaigns. Refer to the [link](/docs/developer-guide/ios-sdk/data-tracking/basic/tracking-user-attributes) to know more.
# 7. Tracking Events
* Make sure you are tracking all the events based on which you can track the user behavior in the app and later use the same to create a campaign targeting appropriate users. Refer to the [link](/docs/developer-guide/ios-sdk/data-tracking/basic/tracking-events) to know more.
# Compliance
Source: https://moengage.com/docs/developer-guide/ios-sdk/compliance/compliance
Manage data tracking opt-outs and IDFA/IDFV privacy controls in your iOS app using the MoEngage SDK.
# Opt-Out of Data Tracking
To disable data tracking by the SDK use [*disableDataTracking()*](https://moengage.github.io/ios-api-reference/Classes/MoEngageSDKAnalytics.html#/c:@M@MoEngageAnalytics@objc\(cs\)MoEngageSDKAnalytics\(im\)disableDataTracking) method as shown below. Once you have opted out of data tracking you need to explicitly opt-in to start tracking any event OR attributes for the user.
```swift Swift wrap theme={null}
MoEngageSDKAnalytics.sharedInstance.disableDataTracking() //Opt out
MoEngageSDKAnalytics.sharedInstance.enableDataTracking() //Opt in
```
```objective-c Objective C wrap theme={null}
[[MoEngageSDKAnalytics sharedInstance] disableDataTracking]; //Opt out
[[MoEngageSDKAnalytics sharedInstance] enableDataTracking]; //Opt in
```
# IDFA and IDFV OptOuts
SDK tracks [IDFA](https://developer.apple.com/documentation/adsupport/asidentifiermanager/1614151-advertisingidentifier)(Advertising Identifier) by default as a UserAttribute, it's tracked only if the AdSupport, AppTrackingTransparency frameworks is included in the project and if the User has not limited Ad Tracking. In case you would want to opt-out of IDFA Tracking call the opt-out method as shown below:
```swift Swift wrap theme={null}
MoEngageSDKAnalytics.sharedInstance.disableIDFATracking() //Opt out
MoEngageSDKAnalytics.sharedInstance.enableIDFATracking() //Opt in
```
```objective-c Objective C wrap theme={null}
[[MoEngageSDKAnalytics sharedInstance] disableIDFATracking]; //Opt out
[[MoEngageSDKAnalytics sharedInstance] enableIDFATracking]; // Opt in
```
* Calling `MoEngageSDKAnalytics.sharedInstance.enableIDFATracking()` requires the `AdSupport` framework to be linked in your project. If the framework is missing, the SDK throws a fatal exception and crashes the app in `DEBUG` builds. In release builds, the call is dropped silently and IDFA is not tracked.
* If you are upgrading an existing app and these strict `DEBUG` validations cause disruptive crashes while you refactor your tracking code, you can temporarily opt out by calling `disableIntegrationValidator()` during SDK initialization. Use this strictly as a stopgap for phased upgrades, and aim to remove the opt-out once your attribute call sites are properly validated.
SDK also tracks [IDFV](https://developer.apple.com/documentation/uikit/uidevice/1620059-identifierforvendor)(Identifier for Vendor) by default as a device Identifier. In case you would want to opt-out of IDFV Tracking call the opt-out method as shown below:
```swift Swift wrap theme={null}
MoEngageSDKAnalytics.sharedInstance.disableIDFVTracking() //Opt out
MoEngageSDKAnalytics.sharedInstance.enableIDFVTracking() //Opt in
```
```objective-c Objective C wrap theme={null}
[[MoEngageSDKAnalytics sharedInstance] disableIDFVTracking]; //Opt out
[[MoEngageSDKAnalytics sharedInstance] enableIDFVTracking]; //Opt in
```
IDFA and IDFV opt-outs are available from SDK version 6.1.1
# Enable/Disable SDK
If you don't want the MoEngage SDK to track any user information or send any data to the MoEngage System use the below method:
```swift Swift theme={null}
MoEngage.sharedInstance.disableSDK()
```
```objective-c Objective C theme={null}
[[MoEngage sharedInstance] disableSDK];
```
Once this API is called all the SDK APIs will be non-operational. SDK will be disabled until [*enableSDK()*](https://moengage.github.io/ios-api-reference/Classes/MoEngage.html#/c:@M@MoEngageSDK@objc\(cs\)MoEngage\(im\)enableSDK) is called.
Once you have the user's consent use the below API to enable the SDK.
```swift Swift theme={null}
MoEngage.sharedInstance.enableSDK()
```
```objective-c Objective C theme={null}
[[MoEngage sharedInstance] enableSDK];
```
Enable/Disable SDK methods are available from SDK version 6.3.0.
# Personalize Experience Events tracking
Source: https://moengage.com/docs/developer-guide/ios-sdk/data-tracking/advanced/personalize-experience-events-tracking
Report impression and click events for experiences from the MoEngage Personalize API in your iOS app.
This document outlines the new methods available in the MoEngage iOS SDK to report impressions and clicks for experiences fetched using the [MoEngage Personalize API](https://www.moengage.com/docs/api/experiences/fetch-experience). These methods are currently available only for the **iOS SDK**.
# Prerequisites
## SDK version
You must update your Native iOS SDK version to **10.01.0** or higher.
## MoEngage Account Configuration
Ensure your MoEngage workspace is enabled to utilize the Personalize API. Refer to [this article](https://www.moengage.com/docs/user-guide/personalize/server-side-personalization/create-server-side-personalization-experience) for details on setting up Personalize API experiences.
# Reporting Experience Shown events
The SDK provides helper APIs to track shown events, refer the [API documentation](https://moengage.github.io/ios-api-reference/Classes/MoEngageSDKAppPersonalization.html) for more details.
Impressions should be reported when an experience is visually presented to the user.
## Single Experience
To report an impression for a single experience, pass **experienceContext** of the experience as a map.
**experienceContext** is a JSON object that is returned as part of the response [of the Personalize API request](https://www.moengage.com/docs/api/experiences/fetch-experience#response-experiences-additional-properties-experience-context).
```swift Swift wrap theme={null}
MoEngageSDKAppPersonalization.sharedInstance.experienceShown(experienceAttribute: experienceContext)
```
```objective-c Objective C wrap theme={null}
[[MoEngageSDKAppPersonalization sharedInstance] experienceShownWithExperienceAttribute:experienceContext];
```
## Mulitple Experiences
To track the experience shown event for multiple experiences use, pass the **list** of **experienceContext** of each experience as a map.
```swift Swift wrap theme={null}
MoEngageSDKAppPersonalization.sharedInstance.experienceShown(experienceAttributes: [experienceContext1, experienceContext2, experienceContext3])
```
```objective-c Objective C wrap theme={null}
[[MoEngageSDKAppPersonalization sharedInstance] experienceShownWithExperienceAttributes:@[experienceContext1, experienceContext2, experienceContext3]];
```
# Reporting Experience Clicked events
The SDK provides helper APIs to track clicked events, refer the [API documentation](https://moengage.github.io/ios-api-reference/Classes/MoEngageSDKPersonalize.html) for more details.
Clicked events should be reported when a user clicks on any element that has been personalized using the response of the Personalize API.
* When you call `experienceClicked()`, the `experienceAttribute` dictionary must contain both `cid` and `experience` keys. If any key is missing, or the call is made against an un-initialized Workspace ID, the SDK throws a fatal exception and crashes the app in `DEBUG` builds. In Release and TestFlight builds, the click event is dropped silently and logged.
* If you are upgrading an existing app and these strict `DEBUG` validations cause disruptive crashes while you refactor your tracking code, you can temporarily opt out by calling `disableIntegrationValidator()` during SDK initialization. Use this strictly as a stopgap for phased upgrades, and aim to remove the opt-out once your attribute call sites are properly validated.
## Single Experience
To report a click event for a single experience, pass the **experienceContext** of the experience as a map.
```swift Swift wrap theme={null}
MoEngageSDKAppPersonalization.sharedInstance.experienceClicked(experienceAttribute: experienceContext)
```
```objective-c Objective C wrap theme={null}
[[MoEngageSDKAppPersonalization sharedInstance] experienceClickedWithExperienceAttribute:experienceContext];
```
## Multiple Experiences
To track the experience shown event for multiple experiences use, pass the **array** of **experienceContext** of each experience as a map.
```swift Swift wrap theme={null}
MoEngageSDKAppPersonalization.sharedInstance.experienceClicked(experienceAttributes: [experienceContext1, experienceContext2, experienceContext3])
```
```objective-c Objective C wrap theme={null}
[[MoEngageSDKAppPersonalization sharedInstance] experienceClickedWithExperienceAttributes:@[experienceContext1, experienceContext2, experienceContext3]];
```
You can optionally include a **b\_id** key in the **experienceContext** object to provide additional context about the click. Its value should describe the specific component or interaction within the experience that was clicked. This is particularly useful for experiences composed of multiple interactive elements, helping to distinguish between clicks on different parts of the same overall experience.
# Session and Source Tracking
Source: https://moengage.com/docs/developer-guide/ios-sdk/data-tracking/advanced/session-and-source-tracking
Track user sessions and deep-link source information in your iOS app using the MoEngage SDK.
From [SDK Version 5.2.2](/docs/release-notes/sdks/ios) we have started supporting Session and Source Tracking, and this is enabled by default in the SDK.
**Note**
* To view Session And Source information tracked in the dashboard, get the same enabled by contacting the MoEngage support team.
* To track source information accurately, make use of the UTM parameters in your deep links and push notification payloads.
# Capture Deep-link Source
**AppDelegate Swizzling**
Calling of [*processURL(\_:)*](https://moengage.github.io/ios-api-reference/Classes/MoEngageSDKAnalytics.html#/c:@M@MoEngageAnalytics@objc\(cs\)MoEngageSDKAnalytics\(im\)processURL:) method is not required if AppDelegate Swizzling is enabled for SDK. For more info on AppDelegate Swizzling, refer to this [link](/docs/developer-guide/ios-sdk/push/basic/ios-push-integration-tutorial#app-delegate-method-swizzling).
For capturing source information via the deep-link call [*processURL(\_:)*](https://moengage.github.io/ios-api-reference/Classes/MoEngageSDKAnalytics.html#/c:@M@MoEngageAnalytics@objc\(cs\)MoEngageSDKAnalytics\(im\)processURL:) method in all the AppDelegate callbacks that you receive when a link is opened, please refer to the code block below.
```swift Swift wrap theme={null}
//MARK:- Deeplinks Processing
func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool {
MoEngageSDKAnalytics.sharedInstance.processURL(url)
//rest of the implementation
return true
}
func application(_ application: UIApplication,
continue userActivity: NSUserActivity,
restorationHandler: @escaping ([UIUserActivityRestoring]?) -> Void) -> Bool
{
if userActivity.activityType == NSUserActivityTypeBrowsingWeb ,
let incomingURL = userActivity.webpageURL{
MoEngageSDKAnalytics.sharedInstance.processURL(incomingURL)
}
//rest of the implementation
return true;
}
//MARK:- Methods Deprecated from iOS9
func application(_ application: UIApplication, handleOpen url: URL) -> Bool {
MoEngageSDKAnalytics.sharedInstance.processURL(url)
//rest of the implementation
return true
}
func application(_ application: UIApplication, open url: URL, sourceApplication: String?, annotation: Any) -> Bool {
MoEngageSDKAnalytics.sharedInstance.processURL(url)
//rest of the implementation
return true
}
```
# Non-Interactive Event
Events that should not affect the session duration calculation in anyways in MoEngage Analytics should be marked as a Non-Interactive event.
These events:
* Do not start a new session, even when the app is in the foreground
* Do not extend the session
* Do not have information on source and session
For example, events that are tracked when the app is in the background to refresh the app content, are not initiated by users and hence can be marked as non-interactive. To mark an event as non-interactive use [*setNonInteractive()*](https://moengage.github.io/ios-api-reference/Classes/MoEngageProperties.html#/c:@M@MoEngageAnalytics@objc\(cs\)MoEngageProperties\(im\)setNonInteractive) method of [*MoEngageProperties*](https://moengage.github.io/ios-api-reference/Classes/MoEngageProperties.html) while tracking the event as shown below:
```swift Swift wrap theme={null}
//Set Attributes
let dict = ["NewsCategory":"Politics"]
let properties = MoEngageProperties(withAttributes: dict)
properties.addDateAttribute(Date(), withName:"refreshTime")
//Set the Event as Non-Interactive
properties.setNonInteractive()
//Track event
MoEngageSDKAnalytics.sharedInstance.trackEvent("App Content Refreshed", withProperties: properties)
```
```objective-c Objective C wrap theme={null}
//Set Attributes
NSMutableDictionary *eventDict = [NSMutableDictionary dictionary];
eventDict[@"NewsCategory"] = @"Politics";
MoEngageProperties* properties = [[MoEngageProperties alloc] initWithAttributes:eventDict];
[properties addDateAttribute:[NSDate date] withName:@"refreshTime"];
//Set the Event as Non-Interactive
[properties setNonInteractive];
//Track event
[[MoEngageSDKAnalytics sharedInstance] trackEvent:@"App Content Refreshed" withProperties:properties];
```
# Tracking Locale
Source: https://moengage.com/docs/developer-guide/ios-sdk/data-tracking/advanced/tracking-locale
Track the locale settings of your user's device in your iOS app using the MoEngage SDK.
For tracking the locale settings of the user device use [*trackLocale()*](https://moengage.github.io/ios-api-reference/Classes/MoEngageSDKAnalytics.html#/c:@M@MoEngageAnalytics@objc\(cs\)MoEngageSDKAnalytics\(im\)trackLocale) method as shown below:
```swift Swift wrap theme={null}
MoEngageSDKAnalytics.sharedInstance.trackLocale()
```
```objective-c Objective C wrap theme={null}
[[MoEngageSDKAnalytics sharedInstance] trackLocale];
```
# Install/Update differentiation
Source: https://moengage.com/docs/developer-guide/ios-sdk/data-tracking/basic/install-update-differentiation
Differentiate between app installs and updates in your iOS app using the MoEngage appStatus API.
Since you might integrate us when your app is already on the App Store, we would need to know whether your app update would be an actual **UPDATE** or an **INSTALL**. Have a logic in place to differentiate between the two, and use the methods below to let the SDK know about the same:
```swift Swift wrap theme={null}
//For Fresh Install of App
MoEngageSDKAnalytics.sharedInstance.appStatus(.install)
// For Existing user who has updated the app
MoEngageSDKAnalytics.sharedInstance.appStatus(.update)
```
```objective-c Objective C theme={null}
//For Fresh Install of App
[[MoEngageSDKAnalytics sharedInstance]appStatus:MoEngageAppStatusInstall];
// For Existing user who has updated the app
[[MoEngageSDKAnalytics sharedInstance]appStatus:MoEngageAppStatusUpdate];
```
# Setting Unique Id for SDK versions below 9.23.0
Source: https://moengage.com/docs/developer-guide/ios-sdk/data-tracking/basic/setting-unique-id-for-sdk-versions-below-9-23-0
Set a unique user ID for login and logout in MoEngage iOS SDK versions below 9.23.0.
# User Login / Logout
It is important that you handle user login and logout as mentioned below. There is a definite possibility that your data gets corrupted if this is not done properly.
* Make sure to get hold of a `unique id` for your app users and pass that information to our SDK using the [*setUniqueID(\_:)*](https://moengage.github.io/ios-api-reference/Classes/MoEngageSDKAnalytics.html#/c:@M@MoEngageAnalytics@objc\(cs\)MoEngageSDKAnalytics\(im\)setUniqueID:) \[Link]method. We use this `unique id` to identify a user and also to merge user profiles across installs and platforms.
* And also, once the user logs out of your app, it is necessary to call [*resetUser()*](https://moengage.github.io/ios-api-reference/Classes/MoEngageSDKAnalytics.html#/c:@M@MoEngageAnalytics@objc\(cs\)MoEngageSDKAnalytics\(im\)resetUser) \[Link]of SDK so that we create a new anonymous user and track the events following this to the new user's profile.
Kindly ensure you call the following methods on user login/logout.
## Login
```swift Swift wrap theme={null}
MoEngageSDKAnalytics.sharedInstance.setUniqueID(UNIQUE_ID) // UNIQUE_ID is used to uniquely identify a user.
```
```objective-c Objective C wrap theme={null}
[[MoEngageSDKAnalytics sharedInstance] setUniqueID:UNIQUE_ID]; // UNIQUE_ID is used to uniquely identify a user.
```
The following values are not allowed in the UniqueID field: "unknown", "guest", "null", "0", "1", "true", "false", "user\_attribute\_unique\_id", "(empty)", "na", "n/a", "", "dummy\_seller\_code", "user\_id", "id", "customer\_id", "uid", "userid", "none", "-2", "-1", "2"
**UNIQUE ID chaos**
* When you go live with MoEngage iOS SDK for the first time, please ensure that you are setting the **Unique ID** of the already logged-in user along with other user attributes.
* Kindly make sure that you are not using a single unique id for all the users, this can happen if you hard code the value, instead of fetching it from your servers.
* If you pass 2 different unique id information without calling [*resetUser()*](https://moengage.github.io/ios-api-reference/Classes/MoEngageSDKAnalytics.html#/c:@M@MoEngageAnalytics@objc\(cs\)MoEngageSDKAnalytics\(im\)resetUser) method in between, the SDK will internally force logout the existing user.
## Logout
```swift Swift wrap theme={null}
MoEngageSDKAnalytics.sharedInstance.resetUser()
```
```objective-c Objective C wrap theme={null}
[[MoEngageSDKAnalytics sharedInstance] resetUser];
```
# Updating User Attribute Unique ID
**Important**
Please make sure that you use [*setAlias(\_:)*](https://moengage.github.io/ios-api-reference/Classes/MoEngageSDKAnalytics.html#/c:@M@MoEngageAnalytics@objc\(cs\)MoEngageSDKAnalytics\(im\)setAlias:)\[Link] for updating the User Attribute Unique ID and not [*setUniqueID(\_:)*](https://moengage.github.io/ios-api-reference/Classes/MoEngageSDKAnalytics.html#/c:@M@MoEngageAnalytics@objc\(cs\)MoEngageSDKAnalytics\(im\)setUniqueID:) \[Link]as calling [*setUniqueID(\_:)*](https://moengage.github.io/ios-api-reference/Classes/MoEngageSDKAnalytics.html#/c:@M@MoEngageAnalytics@objc\(cs\)MoEngageSDKAnalytics\(im\)setUniqueID:) \[Link]with a new value will reset the current user and lead to the creation of unintended users in our system.
In a scenario where you have to update the existing user's Unique ID value make use of [*setAlias(\_:)*](https://moengage.github.io/ios-api-reference/Classes/MoEngageSDKAnalytics.html#/c:@M@MoEngageAnalytics@objc\(cs\)MoEngageSDKAnalytics\(im\)setAlias:) \[Link]method as shown below with the updated Unique ID value:
```swift Swift wrap theme={null}
MoEngageSDKAnalytics.sharedInstance.setAlias(UPDATED_UNIQUE_ID)
```
```objective-c Objective C wrap theme={null}
[[MoEngageSDKAnalytics sharedInstance] setAlias:UPDATED_UNIQUE_ID];
```
# Tracking events
Source: https://moengage.com/docs/developer-guide/ios-sdk/data-tracking/basic/tracking-events
Track custom user events and their attributes in your iOS app using the MoEngage SDK.
Event tracking is used to track user behavior in an app. And later based on the same tracked behavior you can target those users for sending relevant notifications. Make sure to track all the events relevant to your business, so that your product managers and marketers can segment your app users and create targeted campaigns. For eg. You can track what a user is purchasing, whether has a user added an item to the cart etc.
* We track certain events by default in our SDK, so please make sure to use the default events instead of tracking a new event for the same scenarios. Find the list of default events tracked by SDK [here](/docs/user-guide/getting-started/integration/default-ios-sdk#default-events).
* SDK adheres to the MoEngage FUP policies. For more information, refer to the [Fair Usage Policy](/docs/user-guide/data/key-concepts/fair-usage-policy-fup).
# How to track events?
Every event has 2 parts to it, the "**name**" of the event and the **properties/attributes** of the event. You have to make use of [*MoEngageProperties*](https://moengage.github.io/ios-api-reference/Classes/MoEngageProperties.html) to track events and their attributes.
For eg. The following code tracks a `Successful Purchase` event. We are including attributes like the Product Name, a Brand Name that describes the event we are tracking.
```swift Swift wrap theme={null}
var eventAttrDict : Dictionary = Dictionary()
eventAttrDict["ProductName"] = "iPhone XS Max"
eventAttrDict["BrandName"] = "Apple"
eventAttrDict["Items In Stock"] = 109
let eventProperties = MoEngageProperties(withAttributes: eventAttrDict)
eventProperties.addAttribute(87000.00, withName: "price")
eventProperties.addAttribute("Rupees", withName: "currency")
eventProperties.addAttribute(true, withName: "in_stock")
eventProperties.addDateEpochAttribute(1439322197, withName: "Time added to cart")
eventProperties.addDateISOStringAttribute("2020-02-22T12:37:56Z", withName: "Time of checkout")
eventProperties.addDateAttribute(Date(), withName: "Time of purchase")
eventProperties.addLocationAttribute(MoEngageGeoLocation.init(withLatitude: 12.23, andLongitude: 9.23), withName: "Pickup Location")
/// JSON is supported from MoEngage-iOS-SDK v9.17.5
eventProperties.addAttribute(["merchantId": "abcdef","business_model": [ "admin_email": "abc@email.com","admin_comment": "payment" ]],withName: "merchant")
eventProperties.addAttribute([["merchantId": "abcdef", "business_model": ["admin_email": "abc@email.com", "admin_comment": "first"]], ["merchantId": "ghijk", "business_model": ["admin_email": "def@email.com", "admin_comment": "second"]]], withName: "retries")
MoEngageSDKAnalytics.sharedInstance.trackEvent("Successful Purchase", withProperties: eventProperties)
```
```objective-c Objective C wrap theme={null}
// track event example
NSMutableDictionary* eventAttrDict = [NSMutableDictionary dictionary];
eventAttrDict[@"ProductName"] = @"iPhone XS Max";
eventAttrDict[@"BrandName"] = @"Apple";
eventAttrDict[@"Items In Stock"] = @109;
MoEngageProperties* eventProperties = [[MoEngageProperties alloc] initWithAttributes:eventAttrDict];
[eventProperties addAttribute:@(87000.00) withName:@"price"];
[eventProperties addAttribute:@"Rupees" withName:@"currency"];
[eventProperties addAttribute:[NSNumber numberWithBool:true] withName:@"in_stock"];
[eventProperties addDateEpochAttribute:1439322197 withName:@"Time added to cart"];
[eventProperties addDateISOStringAttribute:@"2020-02-22T12:37:56Z" withName:@"Time of checkout"];
[eventProperties addDateAttribute:[NSDate date] withName:@"Time of purchase"];
MoEngageGeoLocation* pickupLocation = [[MoEngageGeoLocation alloc] initWithLatitude:12.23 andLongitude:9.23];
[eventProperties addLocationAttribute:pickupLocation withName:@"Pickup Location"];
/// JSON is supported from MoEngage-iOS-SDK v9.17.5
[eventProperties addAttribute:@{@"merchantId": @"abcdef",@"business_model": @{@"admin_email": @"abc@email.com",@"admin_comment": @"payment"}} withName:@"merchant"];
[eventProperties addAttribute:@[@{@"merchantId":@"abcdef",@"business_model":@{@"admin_email":@"abc@email.com",@"admin_comment":@"first"}},@{@"merchantId":@"ghijk",@"business_model":@{@"admin_email":@"def@email.com",@"admin_comment":@"second"}}] withName:@"retries"];
[[MoEngageSDKAnalytics sharedInstance] trackEvent:@"Successful Purchase" withProperties:eventProperties];
```
Starting with iOS SDK [11.0.0](/docs/release-notes/sdks/ios#22nd-july-2026):
* `MoEngageProperties(withAttributes:)` and `addAttribute(_:withName:)` require values to conform to `Sendable` for Swift concurrency safety (`Any` → `any Sendable`). The literal values used above (strings, numbers, booleans, dictionaries of these) already conform to `Sendable`, so this example compiles unchanged. Under the Swift 6 language mode or strict concurrency, non-`Sendable` values need to be updated.
* `trackEvent(_:withProperties:)` returns a typed task object (`MoEngageTrackEventTask`) instead of `Void`, so you can observe per-call success or failure through `.onSuccess` / `.onFailure` or the async `result()` method. Direct calls like the one above compile unchanged.
**Non-Interactive Events**
Events that should not affect the session duration calculation in anyways in MoEngage Analytics should be marked as a Non-Interactive events. Refer to [this](https://www.moengage.com/docs/developer-guide/ios-sdk/data-tracking/advanced/session-and-source-tracking) for more info on the same.
If you don’t have any attributes, just pass **nil** as the second argument. For eg.
```swift Swift wrap theme={null}
MoEngageSDKAnalytics.sharedInstance.trackEvent("Event Name", withProperties: nil)
```
```objective-c Objective C wrap theme={null}
[[MoEngageSDKAnalytics sharedInstance] trackEvent:@"Event Name" withProperties:nil];
```
## Validations and restrictions
Event attributes have two layers of validation that apply across both `DEBUG` and release builds:
* **Naming and format rules** — these always apply, regardless of build configuration. Refer to [Naming and format rules](#naming-and-format-rules) below.
* **Type validation** — invalid attribute values (unsupported types such as `null`, custom models, `NaN`, `Infinity`, empty collections, or `Date`/`MoEngageGeoLocation` nested inside an Array or Dictionary) cause a fatal exception in `DEBUG` builds and are silently dropped from the payload in release builds. The rest of the event is tracked. Refer to [Supported attribute value types](#supported-attribute-value-types) below.
In the iOS SDK, `DEBUG` mode means the SDK was initialized using `initializeDefaultTestInstance(_:)` (TEST workspace) and the app is running attached to Xcode. This is different from a release build that has debug symbols enabled.
### Naming and format rules
* **Attribute names must be non-empty.** Passing an empty string (`""`) as an attribute name causes a fatal exception in `DEBUG` builds. In release builds, the attribute is dropped.
* **Reserved prefixes.** You cannot use `moe_` as a prefix when naming events, event attributes, or user attributes. It is a system prefix, and using it might result in periodic blocklisting without prior communication.
### Supported attribute value types
Attribute values must be one of: `String`, `Number`, `Date`, `MoEngageGeoLocation`, `Dictionary` (with string keys and supported values), or `Array` (of supported values). If an unsupported value is passed:
* In `DEBUG` builds, the SDK throws a fatal exception and crashes the app to surface data issues early in development.
* In Release and TestFlight builds, the SDK silently drops the specific invalid attribute and logs the issue. The rest of the event payload is still tracked.
Common triggers for the `DEBUG` exception:
* Passing `null` (`NSNull()`).
* Passing custom models or UI elements (for example, `UIColor`, `UIImage`).
* Passing invalid numbers like `NaN` or `Infinity`.
* Passing empty Arrays or Dictionaries.
* Nesting `Date` or `MoEngageGeoLocation` objects inside an Array or Dictionary. These must be passed directly as values.
If you are upgrading an existing app and these strict `DEBUG` validations cause disruptive crashes while you refactor your tracking code, you can temporarily opt out by calling `disableIntegrationValidator()` during SDK initialization. Use this strictly as a stopgap for phased upgrades, and aim to remove the opt-out once your attribute call sites are properly validated.
### Filtering unsupported attribute values
If you are upgrading to a newer version of the iOS SDK and your app passes attribute values whose types are not validated at the call site, add a type-check guard before building the `MoEngageProperties` object. This prevents `DEBUG` crashes and ensures only valid data reaches the SDK in all builds.
```swift Swift wrap theme={null}
// Helper to check if a value is a supported event attribute type.
// Call this before passing any dynamically-typed value to addAttribute(_:withName:).
// Starting with iOS SDK 11.0.0, addAttribute(_:withName:) requires `any Sendable` instead of `Any`.
func isSupportedEventAttributeValue(_ value: any Sendable) -> Bool {
switch value {
case let d as Double: return !d.isNaN && !d.isInfinite
case let f as Float: return !f.isNaN && !f.isInfinite
case is String, is Bool, is Int, is Int8, is Int16, is Int32, is Int64,
is UInt, is UInt8, is UInt16, is UInt32, is UInt64, is NSNumber:
return true
case let arr as [Any]: return !arr.isEmpty
case let dict as [String: Any]: return !dict.isEmpty
default: return false
}
}
// Usage — value comes from a server response, external model, or unknown source
let properties = MoEngageProperties()
if isSupportedEventAttributeValue(dynamicValue) {
properties.addAttribute(dynamicValue, withName: "attribute_name")
}
```
```objective-c Objective C wrap theme={null}
// Check if a value is a supported event attribute type before passing to the SDK.
- (BOOL)isSupportedEventAttributeValue:(id)value {
if ([value isKindOfClass:[NSString class]] || [value isKindOfClass:[NSNumber class]]) {
// Guard against NaN / Infinity for floating-point NSNumbers
if ([value isKindOfClass:[NSNumber class]]) {
double d = [(NSNumber *)value doubleValue];
if (isnan(d) || isinf(d)) return NO;
}
return YES;
}
if ([value isKindOfClass:[NSArray class]]) return [(NSArray *)value count] > 0;
if ([value isKindOfClass:[NSDictionary class]]) return [(NSDictionary *)value count] > 0;
return NO;
}
// Usage
MoEngageProperties *properties = [[MoEngageProperties alloc] init];
if ([self isSupportedEventAttributeValue:dynamicValue]) {
[properties addAttribute:dynamicValue withName:@"attribute_name"];
}
```
`Date` and `MoEngageGeoLocation` values must be passed using the dedicated `addDateAttribute`, `addDateEpochAttribute`, `addDateISOStringAttribute`, and `addLocationAttribute` methods — not through the dictionary-based `withAttributes:` initializer or `addAttribute:withName:`.
# Manual Sync
For syncing the tracked events instantaneously, use the [*flush()*](https://moengage.github.io/ios-api-reference/Classes/MoEngageSDKAnalytics.html#/c:@M@MoEngageAnalytics@objc\(cs\)MoEngageSDKAnalytics\(im\)flush) method as shown below:
```swift Swift wrap theme={null}
MoEngageSDKAnalytics.sharedInstance.flush()
```
```objective-c Objective C wrap theme={null}
[[MoEngageSDKAnalytics sharedInstance] flush];
```
# Testing events after integration
Login to the MoEngage account with the credentials provided for your app.
Look at the top left and Switch to the **Test** environment. Ensure that your testing is done on the test environment to keep the test data separate from the Live data. Ensure you have [Initialized the SDK](https://www.moengage.com/docs/developer-guide/ios-sdk/sdk-integration/basic/sdk-initialization).
After adding event tracking in the app, as shown above, you can visit Dashboard > Recent Events to check whether the events are being tracked.
*Events can take up to 20 minutes to show up in the dashboard*
While testing it is recommended to enable [logs in Debug Mode](https://www.moengage.com/docs/developer-guide/ios-sdk/troubleshooting-and-faqs/troubleshooting-and-faqs-ios).
SDK prints a list of all events which are synced in the current flush, so you can always refer to the logs to check if the events tracked by you are being sent to the backend or not. Also, logs provide info about if the sync with the backend was successful or not. (In case it is unsuccessful, SDK saves all the tracked events and attempts to sync again in the next flush attempt).
# Tracking user attributes
Source: https://moengage.com/docs/developer-guide/ios-sdk/data-tracking/basic/tracking-user-attributes
Track user attributes and set unique identifiers for cross-platform identification in the iOS SDK.
User Attributes are pieces of information you know about a user. They could be demographics like age or gender, account-specific like plan, or whether a user has seen a particular A/B test variation. User attributes are a customer identity you can reference throughout the customer’s lifetime.
# Identifying Users
For SDK versions below 9.23.0 refer to [this document](https://www.moengage.com/docs/developer-guide/ios-sdk/data-tracking/basic/setting-unique-id-for-sdk-versions-below-9-23-0).
Setting identifiers is important to:
* To tie user behavior across platforms, i.e., iOS, Android, Web, etc.
* This is to ensure unnecessary or stale users are not created.
* To identify users across installs/re-installs.
## Single Identifier
Call the API below to pass the identifier on to the MoEngage SDK.
```swift Swift wrap theme={null}
MoEngageSDKAnalytics.sharedInstance.identifyUser(identity: "identifier")
```
```objective-c Objective C wrap theme={null}
[[MoEngageSDKAnalytics sharedInstance] identifyUserWithIdentity:@"identifier" workspaceId:nil];
```
This method is a replacement for the deprecated ***setUniqueId()***. If you are using \*\*\*setUniqueId() \*\*\*in your application, consider replacing it with ***identifyUser()**\*\*.*
## Multiple Identifiers
If your application has multiple identifiers using which you identify a user you can pass all the identifiers to the SDK using the below API.
```swift Swift wrap theme={null}
MoEngageSDKAnalytics.sharedInstance.identifyUser(
identities: [
"identifierName1": "identifierValue1", "identifierName2": "identifierValue2"
]
)
```
```objective-c Objective C wrap theme={null}
[[MoEngageSDKAnalytics sharedInstance] identifyUserWithIdentities:@{
@"identifierName1": @"identifierValue1", @"identifierName2": @"identifierValue2"
} workspaceId:nil];
```
If you call `identifyUser()` multiple times with different identifier names, the SDK will append this identifier to the already set identifiers.
**Information**
Updates are made to SDK functions to improve user identification and session management.
* **Forced Logout**: The MoEngage SDK no longer automatically logs out the previous user when a new user is detected on the device. Logout should now be explicitly called for workspaces enabled with Identity resolution to avoid data corruption.
* **SetUniqueID**: *IdentifyUser* function supports multiple identifiers, which replaces the need of using *SetUniqueID* function for user identification. Note that *SetUniqueID* is marked for removal in the future releases of SDK versions - it is important to use *identifyUser* instead especially if you are using Identity resolution in your workspace.
* **SetAlias**: For workspaces with the Identity resolution feature enabled, MoEngage SDK stores the previous identifier values. When *IdentifyUser* function is used to track the new identifier values, MoEngage SDK detects the change in identifier value and reports accordingly.
* If you call the *IdentifyUser* function without logging out, then the existing logged-in user's ID is updated.
Refer to our help [document](/docs/user-guide/data/user-data/unified-identity-identity-resolution) to learn more about the feature.
If a network issue prevents the identifier from reaching the MoEngage server, the SDK caches the `identifyUser()` call and retries automatically once the network is available or the next time the app opens.
For more information, refer to:
* [Enable/Disable SDK](/docs/developer-guide/ios-sdk/compliance/compliance#enabledisable-sdk)
* [Opt-Out of Data Tracking](/docs/developer-guide/ios-sdk/compliance/compliance#opt-out-of-data-tracking)
## Logout
The application needs to notify the MoEngage SDK whenever the user is logged out of the application. Call the API whenever the user is logged out of the application to notify the SDK.
```swift Swift wrap theme={null}
MoEngageSDKAnalytics.sharedInstance.resetUser()
```
```objective-c Objective C wrap theme={null}
[[MoEngageSDKAnalytics sharedInstance] resetUser];
```
* Logout is asynchronous. Identify the next user, set user attributes, and track events only after logout completes.
* Data sent while logout is still in progress is lost or misattributed. Depending on timing, the SDK either records it against the previous user or discards it while clearing that user's data. Use the [logout callback](#logout-callback) to sequence these calls.
Logout clears the user's attributes and identities, then starts a new anonymous user with a new session. The SDK retains the device identifier and push token, so logout does not unregister the device for push. Call `resetUser()` only when the user logs out. Each call creates a new anonymous user and re-registers the device.
### Logout Callback
To receive a callback when logout is complete, observe the task returned by `resetUser()`:
```swift Swift wrap theme={null}
MoEngageSDKAnalytics.sharedInstance.resetUser()
.onSuccess { _ in
// Process your logout completion here
}
.onFailure { failure in
print("Logout failed: \(failure.reason) - \(failure.message)")
}
```
```objective-c Objective C wrap theme={null}
MoEngageResetUserTask *task = [[MoEngageSDKAnalytics sharedInstance] resetUser];
[task onSuccess:^(id _Nonnull result) {
// Process your logout completion here
}];
[task onFailure:^(MoEngageRequestFailure * _Nonnull failure) {
NSLog(@"Logout failed: %@ - %@", failure.reason, failure.message);
}];
```
Starting with iOS SDK [11.00.0](/docs/release-notes/sdks/ios#22nd-july-2026), `resetUser()` returns a typed task object ([`MoEngageResetUserTask`](https://moengage.github.io/ios-api-reference/Classes/MoEngageResetUserTask.html)) instead of `Void`. Observe the outcome through `.onSuccess` / `.onFailure` or the async `result()` method. On SDK versions below 11.00.0, the equivalent is `resetUser(withCompletionBlock:)`; that variant is deprecated from 11.00.0 in favour of the typed task above.
# Default User Attributes
Some default SDK User Attribute can be set for eg. email-id, mobile number, gender, user name, birthday. The default attributes tracked by SDK can be set as shown below:
```swift Swift wrap theme={null}
MoEngageSDKAnalytics.sharedInstance.setName(userName)
MoEngageSDKAnalytics.sharedInstance.setLastName(userLastname)
MoEngageSDKAnalytics.sharedInstance.setFirstName(userFirstName)
MoEngageSDKAnalytics.sharedInstance.setEmailID(userEmailID)
MoEngageSDKAnalytics.sharedInstance.setMobileNumber(userPhoneNo)
MoEngageSDKAnalytics.sharedInstance.setGender(.male) //Use MoEngageUserGender enum
MoEngageSDKAnalytics.sharedInstance.setDateOfBirth(userBirthdate)//userBirthdate should be a Date instance
MoEngageSDKAnalytics.sharedInstance.setLocation(MoEngageGeoLocation(withLatitude: userLocationLat, andLongitude: userLocationLng)) //userLocationLat and userLocationLng are double values of the location coordinates
```
```objective-c Objective C wrap theme={null}
[[MoEngageSDKAnalytics sharedInstance] setName:userName];
[[MoEngageSDKAnalytics sharedInstance] setLastName:userLastname];
[[MoEngageSDKAnalytics sharedInstance] setFirstName:userFirstName];
[[MoEngageSDKAnalytics sharedInstance] setEmailID:userEmailID];
[[MoEngageSDKAnalytics sharedInstance] setMobileNumber:userPhoneNo];
[[MoEngageSDKAnalytics sharedInstance] setGender:MoEngageUserGenderMale]; // Use MoEngageUserGender enum
[[MoEngageSDKAnalytics sharedInstance] setDateOfBirth:userBirthdate];//userBirthdate should be a NSDate instance
[[MoEngageSDKAnalytics sharedInstance] setLocation:[[MoEngageGeoLocation alloc] initWithLatitude:userLocationLat andLongitude:userLocationLng]];//userLocationLat and userLocationLng are double values of the location coordinates
```
* User Phone No / Mobile Number must be tracked as a string to work properly in MoEngage systems.
* For more information on supported data types and data tracking policies, refer to [Data Tracking Policies](/docs/user-guide/data/key-concepts/data-tracking-policies).
For the full list of validation rules and what happens in `DEBUG` versus release builds, refer to [Validations and restrictions](#validations-and-restrictions).
# Custom User Attributes
To set custom attributes just provide custom keys different to the ones present in [here](https://www.moengage.com/docs/user-guide/getting-started/integration/default-ios-sdk#default-user-attributes). Following is an example:
```swift Swift wrap theme={null}
MoEngageSDKAnalytics.sharedInstance.setUserAttribute("Bengaluru", withAttributeName: "Current_city")
MoEngageSDKAnalytics.sharedInstance.setUserAttribute(["Bengaluru","Delhi","Chennai"], withAttributeName: "Current_cities")
```
```objective-c Objective C wrap theme={null}
[[MoEngageSDKAnalytics sharedInstance] setUserAttribute:@"Bengaluru" withAttributeName:@"Current_city"];
NSArray *myArray = @[@"Bengaluru", @"Delhi", @"Chennai"];
[[MoEngageSDKAnalytics sharedInstance] setUserAttribute:myArray withAttributeName:@"Current_cities"];
```
Starting with iOS SDK [11.0.0](/docs/release-notes/sdks/ios#22nd-july-2026), `setUserAttribute(_:withAttributeName:level:forAppID:)` requires the `value` parameter to conform to `Sendable` for Swift concurrency safety (`Any` → `any Sendable`). The literal values used in these examples (strings, arrays, dictionaries of these) already conform to `Sendable`, so they compile unchanged. Under the Swift 6 language mode or strict concurrency, non-`Sendable` values need to be updated.
For the full list of validation rules, refer to [Validations and restrictions](#validations-and-restrictions).
## JSON Attributes
From MoEngage-iOS-SDK ***v9.17.5***, we have added support for JSON and array of JSON user attributes. Following is an example:
```swift Swift wrap theme={null}
MoEngageSDKAnalytics.sharedInstance.setUserAttribute(["item-id" : 123,"item-type" : "books","item-cost" : ["amount" : 100,"currency" : "USD"]],withAttributeName: "product")
MoEngageSDKAnalytics.sharedInstance.setUserAttribute([["item-id" : 123,"item-cost" : ["amount" : 100,"currency" : "USD"]],["item-id" : 323,"item-cost" : ["amount" : 90,"currency" : "USD"]]],withAttributeName: "products")
```
```objective-c Objective C wrap theme={null}
[MoEngageSDKAnalytics.sharedInstance setUserAttribute:@{@"item-id" : @123,@"item-type" : @"books",@"item-cost" : @{@"amount" : @100,@"currency" : @"USD"}} withAttributeName:@"product"];
[MoEngageSDKAnalytics.sharedInstance setUserAttribute:@[@{@"item-id" : @123,@"item-cost" : @{@"amount" : @100,@"currency" : @"USD"}} , @{@"item-id" : @323,@"item-cost" : @{@"amount" : @90,@"currency" : @"USD"}}] withAttributeName:@"products"];
```
# Portfolio-Level User Attributes
**Prerequisites**
* iOS SDK version 10.07.0 or above.
* Multiple projects must be configured in your MoEngage workspace. For more information, refer to [Portfolio](/docs/user-guide/settings/account/portfolio/portfolio) or contact your MoEngage account manager.
In a [Portfolio](/docs/user-guide/settings/account/portfolio/portfolio) enabled workspace, user attributes can be tracked either at the project level (scoped to a specific project) or at the portfolio level (shared across all projects in the workspace).
## Set Portfolio-Level Attributes
To set a user attribute at the portfolio level, use the API shown below. The `level` parameter accepts `.project` (Objective-C: `MoEngageUserAttributeLevelProject`) to scope the attribute to the current project, or `.portfolio` (Objective-C: `MoEngageUserAttributeLevelPortfolio`) to share it across all projects in the portfolio. When you omit the `level` parameter, the attribute defaults to project level.
```swift Swift wrap theme={null}
// Project-level attribute (default — same as existing behaviour)
MoEngageSDKAnalytics.sharedInstance.setUserAttribute("value", withAttributeName: "attribute_name", level: .project)
// Portfolio-level attribute (shared across all projects in the portfolio)
MoEngageSDKAnalytics.sharedInstance.setUserAttribute("value", withAttributeName: "attribute_name", level: .portfolio)
```
```objective-c Objective C wrap theme={null}
// Project-level attribute (default)
[[MoEngageSDKAnalytics sharedInstance] setUserAttribute:@"value"
withAttributeName:@"attribute_name"
level:MoEngageUserAttributeLevelProject];
// Portfolio-level attribute
[[MoEngageSDKAnalytics sharedInstance] setUserAttribute:@"value"
withAttributeName:@"attribute_name"
level:MoEngageUserAttributeLevelPortfolio];
```
Setting a unique ID at the portfolio level is not supported and causes a fatal exception in Xcode debug builds. Use the project-level `identifyUser` APIs to set identifiers. See [Identifying Users](#identifying-users).
# Date and Time User Attributes
Date and time attributes can be set as user attributes. For this refer to the methods in the code block below:
```swift Swift wrap theme={null}
//1. Track UserAttribute using Date instance
MoEngageSDKAnalytics.sharedInstance.setUserAttributeDate(Date(), withAttributeName: "Date Attr 1")
//2. Track UserAttribute using ISO Date String in format "yyyy-MM-dd'T'HH:mm:ss'Z'"
MoEngageSDKAnalytics.sharedInstance.setUserAttributeISODate("2020-01-12T18:45:59Z", withAttributeName: "Date Attr 2")
//3. Track UserAttribute using Epoch value
MoEngageSDKAnalytics.sharedInstance.setUserAttributeEpochTime(663333, withAttributeName: "Date Attr 3")
```
```objective-c Objective C wrap theme={null}
//1. Track UserAttribute using Date instance
[[MoEngageSDKAnalytics sharedInstance] setUserAttributeDate:[NSDate date] withAttributeName:@"DateAttr1"];
//2. Track UserAttribute using ISO Date String in format "yyyy-MM-dd'T'HH:mm:ss'Z'"
[[MoEngageSDKAnalytics sharedInstance] setUserAttributeISODate:@"2020-01-12T18:45:59Z" withAttributeName:@"DateAttr2"];
//3. Track UserAttribute using Epoch value
double timestampEpochValue = [[NSDate date] timeIntervalSince1970];
[[MoEngageSDKAnalytics sharedInstance] setUserAttributeEpochTime:timestampEpochValue withAttributeName:@"LastPurchaseDate"];
```
# Location Attributes
The location of a user or any location can be set as user attribute. For this use [*setLocation(\_:withAttributeName:)*](https://moengage.github.io/ios-api-reference/Classes/MoEngageSDKAnalytics.html#/c:@M@MoEngageAnalytics@objc\(cs\)MoEngageSDKAnalytics\(im\)setLocation:withAttributeName:) method and pass lat, the long value of the location as shown in the following example:
```swift Swift wrap theme={null}
MoEngageSDKAnalytics.sharedInstance.setLocation(MoEngageGeoLocation.init(withLatitude: 72.90909, andLongitude: 12.34567), withAttributeName: "attribute name")
```
```objective-c Objective C wrap theme={null}
[[MoEngageSDKAnalytics sharedInstance] setLocation:[[MoEngageGeoLocation alloc] initWithLatitude:23.33 andLongitude:26.22] withAttributeName:@"attribute name"];
```
# Validations and restrictions
User attributes have two layers of validation that apply across both `DEBUG` and release builds:
* **Naming and format rules** — these always apply, regardless of build configuration. Refer to [Naming and format rules](#naming-and-format-rules) below.
* **Type validation** — invalid attribute values (unsupported types such as `null`, custom models, `NaN`, `Infinity`, empty collections, `NSURL`, or `Date`/`MoEngageGeoLocation` nested inside an Array or Dictionary) cause a fatal exception in `DEBUG` builds and are silently dropped in release builds. Refer to [Supported attribute value types](#supported-attribute-value-types) below.
In the iOS SDK, `DEBUG` mode means the SDK was initialized using `initializeDefaultTestInstance(_:)` (TEST workspace) and the app is running attached to Xcode. This is different from a release build that has debug symbols enabled.
## Naming and format rules
* **Attribute names must be non-empty.** Passing an empty string (`""`) as an attribute name causes a fatal exception in `DEBUG` builds. In release builds, the attribute is dropped.
* **No dot (`.`) in attribute names.**
* **Attribute names must not start with a dollar sign (`$`).**
* **Reserved prefix.** You cannot use `moe_` as a prefix when naming events, event attributes, or user attributes. It is a system prefix, and using it might result in periodic blocklisting without prior communication.
* **Reserved keywords.** Do not use any of the following keys when tracking user attributes — they are reserved for SDK and system use:
* `USER_ATTRIBUTE_UNIQUE_ID`
* `USER_ATTRIBUTE_USER_EMAIL`
* `USER_ATTRIBUTE_USER_MOBILE`
* `USER_ATTRIBUTE_USER_NAME`
* `USER_ATTRIBUTE_USER_GENDER`
* `USER_ATTRIBUTE_USER_FIRST_NAME`
* `USER_ATTRIBUTE_USER_LAST_NAME`
* `USER_ATTRIBUTE_USER_BDAY`
* `USER_ATTRIBUTE_NOTIFICATION_PREF`
* `USER_ATTRIBUTE_OLD_ID`
* `USER_ATTRIBUTE_DND_START_TIME`
* `USER_ATTRIBUTE_DND_END_TIME`
* `MOE_TIME_FORMAT`
* `MOE_TIME_TIMEZONE`
* `MOE_GAID`
* `MOE_ISLAT`
* `INSTALL`
* `UPDATE`
* `status`
* `user_id`
* `source`
## Supported attribute value types
Attribute values must be one of: `String`, `Number`, `Date`, `MoEngageGeoLocation`, `Dictionary`, or `Array` (of strings or numbers). `NSURL` is not supported — convert to a string before passing it.
If an unsupported value is passed:
* In `DEBUG` builds, the SDK throws a fatal exception and crashes the app so you can catch data issues early.
* In release builds, the SDK drops the specific invalid attribute. Other attributes set on the user are unaffected.
Common triggers for the `DEBUG` exception:
* Passing `null` (`NSNull()`).
* Passing custom models or UI elements (for example, `UIColor`, `UIImage`).
* Passing invalid numbers like `NaN` or `Infinity`.
* Passing empty Arrays or Dictionaries.
* Nesting `Date` or `MoEngageGeoLocation` objects inside an Array or Dictionary. These must be passed directly as values.
If you are upgrading an existing app and these strict `DEBUG` validations cause disruptive crashes while you refactor your tracking code, you can temporarily opt out by calling `disableIntegrationValidator()` during SDK initialization. Use this strictly as a stopgap for phased upgrades, and aim to remove the opt-out once your attribute call sites are properly validated.
## Filtering unsupported attribute values
If you are upgrading to a newer version of the iOS SDK and your app passes attribute values whose types are not validated at the call site, add a type-check guard before calling `setUserAttribute`. This prevents `DEBUG` crashes and ensures only valid data reaches the SDK in all builds.
```swift Swift wrap theme={null}
// Helper to check if a value is a supported user attribute type.
// Call this before passing any dynamically-typed value to setUserAttribute(_:withAttributeName:).
func isSupportedUserAttributeValue(_ value: Any) -> Bool {
switch value {
case let d as Double: return !d.isNaN && !d.isInfinite
case let f as Float: return !f.isNaN && !f.isInfinite
case is String, is Bool, is Int, is Int8, is Int16, is Int32, is Int64,
is UInt, is UInt8, is UInt16, is UInt32, is UInt64, is NSNumber:
return true
case let arr as [Any]: return !arr.isEmpty
case let dict as [String: Any]: return !dict.isEmpty
default: return false
}
}
// Usage — value comes from a server response, external model, or unknown source
if isSupportedUserAttributeValue(dynamicValue) {
MoEngageSDKAnalytics.sharedInstance.setUserAttribute(dynamicValue, withAttributeName: "attribute_name")
}
// Values that fail the check (custom objects, NSNull, NaN, empty collections, etc.)
// are skipped without crashing in both DEBUG and Release builds.
```
```objective-c Objective C wrap theme={null}
// Check if a value is a supported user attribute type before passing to the SDK.
- (BOOL)isSupportedUserAttributeValue:(id)value {
if ([value isKindOfClass:[NSString class]] || [value isKindOfClass:[NSNumber class]]) {
if ([value isKindOfClass:[NSNumber class]]) {
double d = [(NSNumber *)value doubleValue];
if (isnan(d) || isinf(d)) return NO;
}
return YES;
}
if ([value isKindOfClass:[NSArray class]]) return [(NSArray *)value count] > 0;
if ([value isKindOfClass:[NSDictionary class]]) return [(NSDictionary *)value count] > 0;
return NO;
}
// Usage
if ([self isSupportedUserAttributeValue:dynamicValue]) {
[[MoEngageSDKAnalytics sharedInstance] setUserAttribute:dynamicValue
withAttributeName:@"attribute_name"];
}
// Unsupported values are skipped without crashing in both DEBUG and Release builds.
```
`Date` and `MoEngageGeoLocation` values must be passed using the dedicated `setUserAttributeDate`, `setUserAttributeISODate`, `setUserAttributeEpochTime`, and `setLocation:withAttributeName:` methods — not through `setUserAttribute:withAttributeName:`.
# iOS SDK Deprecated Versions
Source: https://moengage.com/docs/developer-guide/ios-sdk/deprecated-versions/deprecated-versions
Check the support status of MoEngage iOS SDK major versions: which are current, which are still supported, and which are deprecated, along with deprecation dates.
Use this page to check the support status of MoEngage iOS SDK major versions. It lists which versions are current, which are still supported, and which are deprecated, along with deprecation dates. This is the source of truth for iOS SDK version status. For how the lifecycle works and what deprecation means for your app, see the [SDK Deprecation Policy](/docs/developer-guide/sdk-lifecycle-and-policies/sdk-deprecation-policy). To set up the iOS SDK, see the [integration guide](/docs/developer-guide/ios-sdk/sdk-integration/basic/sdk-integration).
## Version Support Status
**Current** — The latest major version. It receives new features, fixes, and support.
**Supported** — An older major version within its 3-year support window. It continues to receive support.
**Deprecated** — A version outside its support window. It no longer receives fixes or support.
| Major Version | Status | Deprecation Date | Notes |
| ---------------- | ---------- | ---------------- | -------------------------------------------------------------------------------------------------------------- |
| 11.x | Current | TBD | Latest major version. Receives new features, fixes, and support. |
| 10.x | Supported | TBD | Receives support. |
| 9.18.0 and above | Supported | TBD | Receives support. |
| 9.17.5 and below | Deprecated | August 2027 | Support, debugging, and fixes are no longer available. Data continues to flow. Upgrade to the current version. |
MoEngage supports each major version for 3 years from its initial release; deprecation then takes effect the following August. See the [SDK Deprecation Policy — Support Window](/docs/developer-guide/sdk-lifecycle-and-policies/sdk-deprecation-policy#support-window) for details. This table is updated when MoEngage announces further deprecations. To receive advance notice, subscribe to the SDK newsletter and ensure your workspace administrator email is current.
## Understanding Deprecated Status
A deprecated version continues to send and receive data. MoEngage does not block traffic. After the deprecation date, MoEngage no longer debugs, fixes, or supports issues specific to that version, and the resolution for any issue is to upgrade to the current version. All fixes ship in the current version only and are not available on supported or deprecated versions. For the complete policy, see the [SDK Deprecation Policy](/docs/developer-guide/sdk-lifecycle-and-policies/sdk-deprecation-policy).
## Upgrading to a Supported Version
* Refer to the [iOS SDK release notes](/docs/release-notes/sdks/ios) for the current major version changes.
* Use the [iOS SDK release checklist](/docs/developer-guide/ios-sdk/checklist/release-checklist) to plan your upgrade.
* Review the [apple-sdk](https://github.com/moengage/apple-sdk) repository for the latest packages.
* For help, contact your MoEngage Customer Success Manager (CSM) or the Support team.
## Frequently Asked Questions
Apps on deprecated versions continue to run and data continues to flow. However, MoEngage no longer provides support, debugging, or fixes for deprecated versions. Upgrading to the current or a supported version using the [iOS SDK release notes](/docs/release-notes/sdks/ios) and the [iOS SDK release checklist](/docs/developer-guide/ios-sdk/checklist/release-checklist) ensures you receive continued support.
MoEngage sends advance notice 6 to 12 months before the deprecation date through email to workspace administrators, the quarterly SDK newsletter, in-dashboard notices, and SDK release notes. See the [SDK Deprecation Policy](/docs/developer-guide/sdk-lifecycle-and-policies/sdk-deprecation-policy) for the full notification schedule.
# Framework Size Impact
Source: https://moengage.com/docs/developer-guide/ios-sdk/framework-size-impact/framework-size-impact
Review the compressed and uncompressed size impact of each MoEngage iOS SDK framework.
We developed a sample application to evaluate the impact of our SDK size. We then uploaded the application build with various configurations to App Store Connect. This allowed us to estimate both the compressed (the download size of our app) and uncompressed size (the disk space the app occupies on the user's device).
**SDK versions for analysis**
* [MoEngage-iOS-SDK](https://cocoapods.org/pods/MoEngage-iOS-SDK) - 9.14.0
* [MoEngageRichNotification](https://cocoapods.org/pods/MoEngageRichNotification) - 7.13.0
* [MoEngageInApp](https://cocoapods.org/pods/MoEngageInApp) - 4.13.0
* [MoEngageCards](https://cocoapods.org/pods/MoEngageCards) - 4.13.0
* [MoEngageGeofence](https://cocoapods.org/pods/MoEngageGeofence) - 5.13.0
* [MoEngageInbox](https://cocoapods.org/pods/MoEngageInbox) - 2.13.0
* [MoEngageRealTimeTrigger](https://cocoapods.org/pods/MoEngageRealTimeTrigger) - 2.13.0
**iPhone 12 Pro as Reference**
Our analysis excludes the Universal build because Apple doesn't install it on user devices. They optimize each app based on the user's device by installing only the necessary architecture. Therefore, our analysis assumes the iPhone 12 Pro with an operating system version 15.0 as the reference.
# MoEngage SDK Size Impact
The size analysis was done when MoEngage-iOS-SDK was integrated into a dummy sample app.
| Framework | Version | Compressed Size | Uncompressed Size |
| :------------------------------------------ | :-------------- | :-------------- | :---------------- |
| Dummy Sample App | NA | 36 KB | 125 KB |
| MoEngage-iOS-SDK | 9.14.0 | 849 KB | 2.5 MB |
| MoEngage-iOS-SDK + MoEngageInApp | 4.13.0 | 1.1 MB | 3.3 MB |
| MoEngage-iOS-SDK + MoEngageCards | 4.13.0 | 1.2 MB | 3.4 MB |
| MoEngage-iOS-SDK + MoEngageGeofence | 5.13.0 | 898 KB | 2.7 MB |
| MoEngage-iOS-SDK + MoEngageRichNotification | 7.13.0 | 933 KB | 2.8 MB |
| MoEngage-iOS-SDK + MoEngageInbox | 2.13.0 | 1.1 MB | 3.1 MB |
| MoEngage-iOS-SDK + MoEngageRealTimeTrigger | 2.13.0 | 1 MB | 3 MB |
| All Framework | Mentioned above | 1.8 MB | 5.2 MB |
**Overall Size Impact of MoEngage**
* The compressed size is the download size of your app. The uncompressed size is equivalent to the size of the installed app on the device.
* The overall size impact of the [MoEngage-iOS-SDK](https://cocoapods.org/pods/MoEngage-iOS-SDK) (Core SDK) along with [MoEngageInApp](https://cocoapods.org/pods/MoEngageInApp), [MoEngageCards](https://cocoapods.org/pods/MoEngageCards), [MoEngageRichNotification](https://cocoapods.org/pods/MoEngageRichNotification), [MoEngageGeofence](https://cocoapods.org/pods/MoEngageGeofence), [MoEngageInbox](https://cocoapods.org/pods/MoEngageInbox), and [MoEngageRealTimeTrigger](https://cocoapods.org/pods/MoEngageRealTimeTrigger) modules is 1.8 MB on compressed size and 5.2 MB on the install size of the app (uncompressed size).
# Custom Action Handling
Source: https://moengage.com/docs/developer-guide/ios-sdk/in-app-messages/custom-action-handling
Handle deep link callbacks and custom actions from MoEngage in-app messages in your iOS app.
## Deeplink callback in InApp
It is used to navigate users directly to a specific location or content within a mobile app.
### Default Handling
By default, SDK passes the deep link callback to the AppDelegate/SceneDelegate method .
If your application is running below iOS 13, then deep link callback is received in the AppDelegate methods:
```swift Swift wrap theme={null}
import UIKit
// Custom Scheme Link
func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool {
//Call only if MoEngageAppDelegateProxyEnabled is NO in Info.plist
MoEngageSDKAnalytics.sharedInstance.processURL(url)
}
// Universal Links
func application(_ application: UIApplication, continue userActivity: NSUserActivity, restorationHandler: @escaping ([UIUserActivityRestoring]?) -> Void) -> Bool {
if userActivity.activityType == NSUserActivityTypeBrowsingWeb ,
let incomingURL = userActivity.webpageURL{
//Call only if MoEngageAppDelegateProxyEnabled is NO in Info.plist
MoEngageSDKAnalytics.sharedInstance.processURL(incomingURL)
}
//rest of the implementation
return true
}
```
```objective-c Objective C wrap theme={null}
// Custom Scheme Link
- (BOOL)application:(UIApplication *)app openURL:(NSURL *)url options:(NSDictionary *)options {
return true;
}
// Universal Link
- (BOOL)application:(UIApplication *)application continueUserActivity:(NSUserActivity *)userActivity restorationHandler:(void (^)(NSArray * _Nullable))restorationHandler {
return true;
}
@end
```
If your application is above iOS 13, then a deeplink callback is received in the below ***SceneDelegate*** method:
```swift Swift wrap theme={null}
import UIKit
// Custom Scheme Link
func scene(_ scene: UIScene, openURLContexts URLContexts: Set) {
let url = URLContexts.first?.url
MoEngageSDKAnalytics.sharedInstance.processURL(url)
}
// Universal Scheme Link
func scene(_ scene: UIScene, continue userActivity: NSUserActivity) {
if let url = userActivity.webpageURL {
MoEngageSDKAnalytics.sharedInstance.processURL(url)
}
}
}
```
### Custom Handling
To receive the deeplink callback in the [*MoEngageInAppNativeDelegate*](https://moengage.github.io/ios-api-reference/Protocols/MoEngageInAppNativeDelegate.html) *,* do pass [MoEngageInAppConfig(shouldProvideDeeplinkCallback: true)](/docs/developer-guide/ios-sdk/sdk-integration/basic/sdk-initialization) while initializing the MoEngageSDKConfig object.
Refer to the [doc](/docs/developer-guide/ios-sdk/in-app-messages/in-app-nativ#non-intrusive-nudges) for callback methods.
**SDK Version**
Custom Deeplink Callback is supported MoEngageInApp @ 6.00.0.
# In-App Nativ
Source: https://moengage.com/docs/developer-guide/ios-sdk/in-app-messages/in-app-nativ
Display contextual in-app messages to your iOS app users using the MoEngage In-App NATIV SDK.
In-App Campaigns are custom views that you can send to a segment of users to show custom messages or give new offers or take to some specific pages. They can be created from your MoEngage account.
**SDK Version**
Follow this doc only if you are using `MoEngage-iOS-SDK` version 8.2.0 and later. If you are using version 5.2.7 or less then follow the doc in this [link](https://developers.moengage.com/hc/en-us/articles/4404155414676).
# SDK Installation
## Install using Swift Package Manager
MoEngageInApp is supported through SPM from SDK version 3.2.0. To integrate, use the GitHub url [https://github.com/moengage/apple-sdk.git](https://github.com/moengage/apple-sdk.git) for SDK versions equal and above 9.23.0, or [https://github.com/moengage/MoEngage-iOS-SDK.git](https://github.com/moengage/MoEngage-iOS-SDK.git) for other SDK versions link and set the branch as master or the required version.
## Install using CocoaPod
**Information**
CocoaPods is being deprecated. MoEngage recommends using Swift Package Manager for all new integrations. For more info on CocoaPods, refer to [CocoaPods Integration Guide](/docs/developer-guide/ios-sdk/sdk-integration/basic/integration-through-cocoa-pods).
Integrate the MoEngageInApp framework by adding the dependency in the pod file as described.
```ruby Ruby theme={null}
pod 'MoEngage-iOS-SDK/InApps',
```
Now run `pod install` to install the framework.
## Manual Integration
**Manual Integration:**
To integrate the `MoEngageInApp` SDK manually to your project follow this [doc](/docs/developer-guide/ios-sdk/manual-integration/manual-integration).
# How to show In-App Message?
MoEngage refreshes the list of eligible In-App campaigns via a meta API call. During an active session, the list refreshes when the app returns from the background to the foreground (at most once every 15 minutes), and immediately when a new session starts or the app is killed and relaunched.
To use In-app Messaging, import `MoEngageInApps` and then add the code below to the view controller(s) in which you want to show the In-app.
```swift Swift wrap theme={null}
import MoEngageInApps
// Add the below line to show inapp
MoEngageSDKInApp.sharedInstance.showInApp()
```
```objective-c Objective-C wrap theme={null}
@import MoEngageInApps;
// Add the below line to show inapp
[[MoEngageSDKInApp sharedInstance] showInApp];
```
Starting with iOS SDK [11.0.0](/docs/release-notes/sdks/ios#22nd-july-2026), `showInApp()` returns a typed task object (`MoEngageShowInAppTask`) instead of `Void`. Direct calls like the one above compile unchanged. Chaining `.onSuccess { ... }` / `.onFailure { ... }` or calling the async `result()` method provides per-call visibility into success or failure.
# Non Intrusive Nudges
Starting with version ***5.0.0***, MoEngage InApp SDK supports displaying Non-Intrusive nudges.
SDK can show Nudges in four positions (i.e. at the top, bottom, bottom left, and bottom right of the screen). Call the [*showNudge(atPosition:)*](https://moengage.github.io/ios-api-reference/Classes/MoEngageSDKInApp.html#/c:@M@MoEngageInApps@objc\(cs\)MoEngageSDKInApp\(im\)showNudgeAtPosition:) in the view controller(s) where you want SDK to show the nudges :
Starting with iOS SDK [11.0.0](/docs/release-notes/sdks/ios#22nd-july-2026), `MoEngageNudgePosition` migrates to a Swift enum (`.top`, `.bottom`, `.bottomLeft`, `.bottomRight`, `.any`, `.none`). Objective-C callers use the renamed `MoEngageNudgePosition*` constants shown above.
```swift Swift wrap theme={null}
//For showing nudges at Top of the screen
MoEngageSDKInApp.sharedInstance.showNudge(atPosition: .top)
//For showing nudges at Bottom of the screen
MoEngageSDKInApp.sharedInstance.showNudge(atPosition: .bottom)
//For showing nudges at BottomLeft of the screen
MoEngageSDKInApp.sharedInstance.showNudge(atPosition: .bottomLeft)
//For showing nudges at BottomRight of the screen
MoEngageSDKInApp.sharedInstance.showNudge(atPosition: .bottomRight)
//For showing nudges at any above mentioned position
MoEngageSDKInApp.sharedInstance.showNudge()
```
```objective-c Objective-C wrap theme={null}
//For showing nudges at Top of the screen
[[MoEngageSDKInApp sharedInstance] showNudgeAtPosition:MoEngageNudgePositionTop];
//For showing nudges at Bottom of the screen
[[MoEngageSDKInApp sharedInstance] showNudgeAtPosition:MoEngageNudgePositionBottom];
//For showing nudges at BottomLeft of the screen
[[MoEngageSDKInApp sharedInstance] showNudgeAtPosition:MoEngageNudgePositionBottomLeft];
//For showing nudges at BottomRight of the screen
[[MoEngageSDKInApp sharedInstance] showNudgeAtPosition:MoEngageNudgePositionBottomRight];
//For showing nudges at any above mentioned position.
[[MoEngageSDKInApp sharedInstance] showNudge];
```
# InApp Callbacks
**Note**
Make sure the class is configured with [*MoEngageInAppNativeDelegate*](https://moengage.github.io/ios-api-reference/Protocols/MoEngageInAppNativeDelegate.html) to receive all the callbacks.
To observe callbacks whenever an inApp is shown, dismissed, or clicked implement [*MoEngageInAppNativeDelegate*](https://moengage.github.io/ios-api-reference/Protocols/MoEngageInAppNativeDelegate.html). Set the delegate using the below methods.
```swift Swift wrap theme={null}
MoEngageSDKInApp.sharedInstance.setInAppDelegate(self)
```
```objective-c Objective-C wrap theme={null}
[[MoEngageSDKInApp sharedInstance] setInAppDelegate:self];
```
Once the delegate is set you will receive the following callbacks:
```swift Swift wrap theme={null}
// Called when an inApp is shown on the screen
func inAppShown(withCampaignInfo inappCampaign: MoEngageInAppCampaign, forAccountMeta accountMeta: MoEngageAccountMeta) {
print("InApp shown callback for Campaign ID(\(inappCampaign.campaign_id)) and CampaignName(\(inappCampaign.campaign_name))")
print("Account Meta AppID: \(accountMeta.appID)")
}
// Called when an inApp is dismissed by the user
func inAppDismissed(withCampaignInfo inappCampaign: MoEngageInAppCampaign, forAccountMeta accountMeta: MoEngageAccountMeta) {
print("InApp dismissed callback for Campaign ID(\(inappCampaign.campaign_id)) and CampaignName(\(inappCampaign.campaign_name))")
print("Account Meta AppID: \(accountMeta.appID)")
}
// Called when an inApp is clicked by the user, and it has been configured with a custom action
func inAppClicked(withCampaignInfo inappCampaign: MoEngageInAppCampaign, andCustomActionInfo customAction: MoEngageInAppAction, forAccountMeta accountMeta: MoEngageAccountMeta) {
print("InApp Clicked with Campaign ID \(inappCampaign.campaign_id)")
print("Custom Actions Key Value Pairs: \(customAction.keyValuePairs)")
}
// Called when an inApp is clicked by the user, and it has been configured with a navigation action
// Below InApp version 6.00.0
func inAppClicked(withCampaignInfo inappCampaign: MoEngageInAppCampaign, andNavigationActionInfo navigationAction: MoEngageInAppAction, forAccountMeta accountMeta: MoEngageAccountMeta) {
print("InApp Clicked with Campaign ID \(inappCampaign.campaign_id)")
print("Navigation Action Screen Name \(navigationAction.screenName) Key Value Pairs: \((navigationAction.keyValuePairs))")
}
// Called when an inApp is clicked by the user, and it has been configured with a navigation action (Deeplink , Navigate To Screen)
// From and above InApp version 6.00.0
func inAppClicked(withCampaignInfo inappCampaign: MoEngageInAppCampaign, andNavigationActionInfo navigationAction: MoEngageInAppNavigationAction, forAccountMeta accountMeta: MoEngageAccountMeta) {
print("InApp Clicked with Campaign ID \(inappCampaign.campaign_id)")
print("Navigation Url \(navigationAction.navigationUrl) Key Value Pairs: \((navigationAction.keyValuePairs))")
print("Navigation Action Type : \(navigationAction.navigationType)")
}
```
```objective-c Objective-C wrap theme={null}
// Called when an inApp is shown on the screen
(void)inAppShownWithCampaignInfo:(MoEngageInAppCampaign *)inappCampaign forAccountMeta:(MoEngageAccountMeta *)accountMeta {
NSLog(@"InApp Shown with Campaign ID %@",inappCampaign.campaign_id);
}
// Called when an inApp is dismissed by the user
- (void)inAppDismissedWithCampaignInfo:(MoEngageInAppCampaign *)inappCampaign forAccountMeta:(MoEngageAccountMeta *)accountMeta {
NSLog(@"InApp Dismissed with Campaign ID %@",inappCampaign.campaign_id);
}
// Called when an inApp is clicked by the user, and it has been configured with a custom action
- (void)inAppClickedWithCampaignInfo:(MoEngageInAppCampaign *)inappCampaign andCustomActionInfo:(MoEngageInAppAction *)customAction forAccountMeta:(MoEngageAccountMeta *)accountMeta {
NSLog(@"InApp Clicked with Campaign ID %@",inappCampaign.campaign_id);
NSLog(@"Custom Action Key Value Pairs: %@", customAction.screenName);
}
// Called when an inApp is clicked by the user, and it has been configured with a navigation action
// Below InApp version 5.03.0
- (void)inAppClickedWithCampaignInfo:(MoEngageInAppCampaign *)inappCampaign andNavigationActionInfo:(MoEngageInAppAction *)navigationAction forAccountMeta:(MoEngageAccountMeta *)accountMeta {
NSLog(@"InApp Clicked with Campaign ID %@",inappCampaign.campaign_id);
NSLog(@"Navigation Action Screen Name %@\n Key Value Pairs: %@", navigationAction.screenName,navigationAction.keyValuePairs);
}
// Called when an inApp is clicked by the user, and it has been configured with a navigation action (Deeplink, Navigate To Screen)
// From and above InApp version 5.03.0
-(void)inAppClickedWithCampaignInfo:(MoEngageInAppCampaign*)inappCampaign andNavigationActionInfo:(MoEngageInAppNavigationAction*)navigationAction forAccountMeta:(nonnull MoEngageAccountMeta *)accountMeta {
NSLog(@"InApp Clicked with Campaign ID %@",inappCampaign.campaign_id);
NSLog(@"Navigation Action Screen Name %@\n Key Value Pairs: %@", navigationAction.navigationUrl,navigationAction.keyValuePairs);
}
```
# Context-Based InApps
We have introduced context-based InApps with SDK version 6.0.0. While creating InApp campaigns you can set the contexts OR tags to the campaign. SDK will check with the current context set in the App and show the inApp only when a current set context matches the campaign context.
## Set Current Context:
To set the current context for the InApp module use [*setCurrentInAppContexts(\_):*](https://moengage.github.io/ios-api-reference/Classes/MoEngageSDKInApp.html#/c:@M@MoEngageInApps@objc\(cs\)MoEngageSDKInApp\(im\)setCurrentInAppContexts:) as shown below:
```swift Swift wrap theme={null}
MoEngageSDKInApp.sharedInstance.setCurrentInAppContexts(["Home","CategoriesScreen"])
```
```objective-c Objective-C wrap theme={null}
[[MoEngageSDKInApp sharedInstance] setCurrentInAppContexts:@[@"Home",@"CategoriesScreen"]];
```
## Reset Context:
To reset the current context for the InApp module call [*invalidateInAppContexts()*](https://moengage.github.io/ios-api-reference/Classes/MoEngageSDKInApp.html#/c:@M@MoEngageInApps@objc\(cs\)MoEngageSDKInApp\(im\)invalidateInAppContexts) method:
```swift Swift wrap theme={null}
MoEngageSDKInApp.sharedInstance.invalidateInAppContexts()
```
```objective-c Objective-C wrap theme={null}
[[MoEngageSDKInApp sharedInstance] invalidateInAppContexts];
```
# Disable In-Apps in ViewController
If you don't want to show InApp messages in a particular ViewController, use [*blockInApp(forViewController:)*](https://moengage.github.io/ios-api-reference/Classes/MoEngageSDKInApp.html#/c:@M@MoEngageInApps@objc\(cs\)MoEngageSDKInApp\(im\)blockInAppForViewController:) method as shown below:
```swift Swift wrap theme={null}
//For not showing in apps in viewController
MoEngageSDKInApp.sharedInstance.blockInApp(forViewController: viewController);
```
```objective-c Objective-C wrap theme={null}
//For not showing in apps in viewController
[[MoEngageSDKInApp sharedInstance] blockInAppForViewController:viewController];
```
# Disabling In-Apps for App
If you do not wish to use InApp messaging, set the property disableInApps. The property has to be set before the initial call.
```swift Swift wrap theme={null}
MoEngageSDKInApp.sharedInstance.disableInApps()
```
```objective-c Objective-C wrap theme={null}
[[MoEngageSDKInApp sharedInstance] disableInApps];
```
# Self handled In-Apps
Self handled In-Apps are not shown by the SDK. While creating the campaign, a String payload has to be provided. The same payload will be provided to the application on campaign delivery. InApp Campaigns that have trigger condition as Screen launch can be fetched using [*getSelfHandledInApp(completionBlock:)*](https://moengage.github.io/ios-api-reference/Classes/MoEngageSDKInApp.html#/c:@M@MoEngageInApps@objc\(cs\)MoEngageSDKInApp\(im\)getSelfHandledInAppWithCompletionBlock:).
```swift Swift wrap theme={null}
MoEngageSDKInApp.sharedInstance.getSelfHandledInApp { campaignInfo, accountMeta in
if let campaignInfo = campaignInfo{ print("Self-Hanled InApp Content \(campaignInfo.campaignContent)")
// Update UI with Self Handled InApp Content
} else{
print("No Self Handled campaign available")
}
}
```
```objective-c objective-c wrap theme={null}
[[MoEngageSDKInApp sharedInstance] getSelfHandledInAppWithCompletionBlock:^(MoEngageInAppSelfHandledCampaign * _Nullable campaignInfo, MoEngageAccountMeta * _Nullable accountMeta) {
if (campaignInfo != nil) {
NSLog(@"Self Handled inApp content : %@", campaignInfo.campaignContent);
// Update UI using the self-handled content
}
else{
NSLog(@"Self-Handled InApp not available");
}
}];
```
For getting the Self-Handled InApp payload in the case of Event-Triggered campaigns, set the [*selfHandledInAppTriggered(withInfo:forAccountMeta:)*](https://moengage.github.io/ios-api-reference/Protocols/MoEngageInAppNativeDelegate.html#/c:@M@MoEngageInApps@objc\(pl\)MoEngageInAppNativeDelegate\(im\)selfHandledInAppTriggeredWithInfo:forAccountMeta:) delegate. SDK will automatically deliver the payload in this delegate if the user is eligible for the campaign.
```swift Swift wrap theme={null}
// This method is called when an event triggers an in-app from the server, which is of type self handled.
func selfHandledInAppTriggered(withInfo inappCampaign: MoEngageInAppSelfHandledCampaign, forAccountMeta accountMeta: MoEngageAccountMeta) {
print("Self Handled InApp Triggered with info:\nCampaign ID:\(inappCampaign.campaign_id) \nContent: \(inappCampaign.campaignContent)")
}
```
```objective-c Objective-C wrap theme={null}
// This method is called when an event triggers an in-app from the server, which is of type self handled.
- (void)selfHandledInAppTriggeredWithInfo:(MoEngageInAppSelfHandledCampaign *)inappCampaign forAccountMeta:(MoEngageAccountMeta *)accountMeta {
NSLog(@"Self Handled InApp Triggered with info:\nCampaign ID: %@ \nContent: %@",inappCampaign.campaign_id, inappCampaign.campaignContent);
}
```
**Note**
The above method will also be called when trying to test the self-handled campaign through a test campaign.
# Self handled multiple In-Apps
Starting with ***MoEngage-iOS-SDK 9.19.0*** version, MoEngage InApp SDK supports displaying Multiple Self Handled InApps. To get multiple self handled inApps for multiple contexts set by the user, use sdk's [*getSelfHandledInApps(completionBlock:)*](https://moengage.github.io/ios-api-reference/Classes/MoEngageSDKInApp.html#/c:@M@MoEngageInApps@objc\(cs\)MoEngageSDKInApp\(im\)getSelfHandledInAppsFor:completionBlock:).
```swift Swift wrap theme={null}
MoEngageSDKInApp.sharedInstance.getSelfHandledInApps(for: "YOUR_WORKSPACE_ID") { [weak self] campaignData in
print(campaignData)
}
```
```objective-c Objective-C wrap theme={null}
[[MoEngageSDKInApp sharedInstance] getSelfHandledInAppsFor:@"YOUR_WORKSPACE_ID" completionBlock:^(MoEngageInAppSelfHandledData * _Nonnull campaignData) {
NSLog(@"%@", campaignData);
}];
```
## Campaign Selection Logic
* **Default Limit**: By default, only 5 campaigns will be fetched.
* **Priority-Based Selection**: Campaigns are delivered based on their priority and last updated time. It checks for priority first and then checks the last updated time on conflicting priorities
* **Exclusion criteria**: Campaigns are only excluded based on specific rules like frequency capping, eligibility criteria, campaign status, or priority limits.
**Example Scenario:** If you have 6 campaigns with different priorities, published time and contexts:
* Context 1: Campaign 1 (P0, T2), Campaign 2 (P1, T3), Campaign 3 (P2, T6)
* Context 2: Campaign 4 (P0, T1), Campaign 5 (P1, T5)
* Context 3: Campaign 6 (P0, T4)
Following campaigns will be delivered in this order: \[Campaign 4, Campaign 1, Campaign 6, Campaign 2, Campaign 5]
**Selection Algorithm:**
1. Filter campaigns by user eligibility and targeting criteria
2. Sort by campaign priority (P0, P1, P2, etc.)
3. For campaigns with same priority, sort by most recent update timestamp
4. Return top 5 campaigns
#### **Best Practices for Campaign Organization for multiple self handled campaigns**
For optimal performance across multiple contexts on a single page, organize your campaigns like this:
* Context 1 (Homepage): Campaign A (P0), Campaign B (P1)
* Context 2 (Product): Campaign C (P0), Campaign D (P1)
* Context 3 (Checkout): Campaign E (P0)
This ensures each context has relevant campaigns without hitting the 5-campaign limit.
Also, make sure that you set the priority of the campaigns you want to fetch accordingly, because the method will fetch all self-handled campaigns regardless of whether they are context-based or not.
## Tracking Self Handled Multiple InApps
The [*getSelfHandledInApps(completionBlock:)*](https://moengage.github.io/ios-api-reference/Classes/MoEngageSDKInApp.html#/c:@M@MoEngageInApps@objc\(cs\)MoEngageSDKInApp\(im\)getSelfHandledInAppsFor:completionBlock:) method returns [*MoEngageInAppSelfHandledData*](https://moengage.github.io/ios-api-reference/Classes/MoEngageInAppSelfHandledData.html), which contains a list of [*MoEngageInAppSelfHandledCampaign*](https://moengage.github.io/ios-api-reference/Classes/MoEngageInAppSelfHandledCampaign.html) objects. The statistics for each [*MoEngageInAppSelfHandledCampaign*](https://moengage.github.io/ios-api-reference/Classes/MoEngageInAppSelfHandledCampaign.html) object must be tracked individually below APIs.
## Fetching Contextual Multiple Self-Handled InApps
To fetch contextual multiple self-handled inapps, set the inapp contexts using [*setCurrentInAppContexts()*](https://moengage.github.io/ios-api-reference/Classes/MoEngageSDKInApp.html#/c:@M@MoEngageInApps@objc\(cs\)MoEngageSDKInApp\(im\)setCurrentInAppContexts:) before calling [*getSelfHandledInApps(completionBlock:)*](https://moengage.github.io/ios-api-reference/Classes/MoEngageSDKInApp.html#/c:@M@MoEngageInApps@objc\(cs\)MoEngageSDKInApp\(im\)getSelfHandledInAppsFor:completionBlock:). This will return a list of contextual and non-contextual inapps(in the order of campaign priority set at the time of campaign creation).
## Tracking InApp Shown And Clicked:
For tracking In-App shown for self-handled in-apps use the [*selfHandledShown(campaignInfo:)*](https://moengage.github.io/ios-api-reference/Classes/MoEngageSDKInApp.html#/c:@M@MoEngageInApps@objc\(cs\)MoEngageSDKInApp\(im\)selfHandledShownWithCampaignInfo:) and provide campaign instance as a parameter:
```swift Swift wrap theme={null}
// Call this method when you show the self handled in-app so we can update impressions.
MoEngageSDKInApp.sharedInstance.selfHandledShown(campaignInfo: campaignInfo)
```
```objective-c Objective-C wrap theme={null}
// Call this method when you show the self handled in-app so we can update impressions.
[[MoEngageSDKInApp sharedInstance] selfHandledShownWithCampaignInfo:campInfo];
```
For tracking InApp Clicked information for stats, call the following methods :
```swift Swift wrap theme={null}
// Call this method to track if self handled in app widget(other than Primary Widget) is clicked.
MoEngageSDKInApp.sharedInstance.selfHandledClicked(campaignInfo: campaignInfo)
// Call this method to track dismiss actions on the inApp.
MoEngageSDKInApp.sharedInstance.selfHandledDismissed(campaignInfo: campaignInfo)
```
```objective-c Objective-C wrap theme={null}
// Call this method to track if self handled in app widget(other than Primary Widget) is clicked.
[[MoEngageSDKInApp sharedInstance] selfHandledClickedWithCampaignInfo:campaignInfo];
// Call this method to track dismiss actions on the inApp.
[[MoEngageSDKInApp sharedInstance] selfHandledDismissedWithCampaignInfo:campaignInfo];
```
# In-App Messaging Rules
We use the following rules while showing the In-App:
Preconditions for inApp to work:
* If InApp Backend Sync was successful in the current session or not.
* Check if InApp is disabled on the current screen.
The following are checked for each campaign in the list of active campaigns(sorted according to priority and Last Updated Time)
* Check Global Delay has lapsed, skip this if Ignore Global Delay set for the campaign.
* Check if the campaign has expired
* Display Rules
* Check Show Only on Screen
* Check with current contexts
* Delivery Controls
* Persistence Check(If primary action of InApp is done but still want to show the inApp)
* Check if the campaign has been shown the maximum number of times.
* Check if the campaign level delay has crossed.
* Check Device Orientation is Portrait for Native InApp and required Orientation for HTML InApp(as selected during campaign creation).
The first campaign satisfying all the rules is shown to the user.
# Actionable Notifications
Source: https://moengage.com/docs/developer-guide/ios-sdk/integration-with-older-version-of-sdk/push/advanced/actionable-notifications
Add custom action buttons to iOS push notifications using MoEngage SDK for older versions.
Actionable notifications let you add custom action buttons to the standard iOS push notifications. Actionable notifications give the user a quick and easy way to perform relevant tasks in response to a notification. These actionable notifications are available from **iOS 8**onwards.
Actionable Notifications are available from MoEngage SDK version 2.2. And to support using UserNotifications framework from iOS 10 onwards use MoEngage SDK 3.0 and above.
# How to implement Actionable Notifications?
To use actionable notification with MoEngage SDK, you have to define the actions and group them into categories as shown in the example. In the example, you can see that we are getting two different Sets of categories, one for iOS10 and above(i.e, set of UNNotificationCategory instances) and the other for the iOS version below iOS10 (i.e, set of MONotificationCategory instances). And while registering for push provide both the sets in parameters as shown below :
```swift Swift theme={null}
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterDelegate{
var window: UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
//--- Rest of Implementation
//For registering for remote notification
let categoriesForiOS10 = self.getCategories()
MoEngage.sharedInstance().registerForRemoteNotification(withCategories: categoriesForiOS10, withUserNotificationCenterDelegate: self)
//--- Rest of Implementation
return true
}
//Example to define categories
//This method gives categories for iOS version 10.0 and above
@available(iOS 10.0, *)
func getCategories() -> Set{
let acceptAction = UNNotificationAction.init(identifier: "ACCEPT_IDENTIFIER", title: "Accept", options: .authenticationRequired)
let declineAction = UNNotificationAction.init(identifier: "DECLINE_IDENTIFIER", title: "Decline", options: .destructive)
let maybeAction = UNNotificationAction.init(identifier: "MAYBE_IDENTIFIER", title: "May Be", options: .foreground)
let inviteCategory = UNNotificationCategory.init(identifier: "INVITE_CATEGORY", actions: [acceptAction,declineAction,maybeAction], intentIdentifiers: [], options: .customDismissAction)
let categoriesSet = Set.init([inviteCategory])
return categoriesSet;
}
}
```
```objectivec Objective-C theme={null}
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOption
{
---------
NSSet* categorySetForiOS10 = [self getNotificationCategories];
[[MoEngage sharedInstance] registerForRemoteNotificationWithCategories:categorySetForiOS10 withUserNotificationCenterDelegate:self];
}
---------
return YES;
}
//Example to define categories
//This method gives categories for iOS version 10.0 and above
-(NSSet*)getNotificationCategories{
UNNotificationAction *acceptAction = [UNNotificationAction actionWithIdentifier:@"ACCEPT_IDENTIFIER" title:@"Accept" options:UNNotificationActionOptionAuthenticationRequired];
UNNotificationAction *declineAction = [UNNotificationAction actionWithIdentifier:@"DECLINE_IDENTIFIER" title:@"Decline" options:(UNNotificationActionOptionDestructive)];
UNNotificationAction *maybeAction = [UNNotificationAction actionWithIdentifier:@"MAYBE_IDENTIFIER" title:@"May Be" options:UNNotificationActionOptionNone];
UNNotificationCategory* inviteCategory = [UNNotificationCategory categoryWithIdentifier:@"INVITE_CATEGORY"actions:@[acceptAction,maybeAction, declineAction,opt4Action] intentIdentifiers:@[] options:UNNotificationCategoryOptionNone];
NSSet *categories = [NSSet setWithObjects:inviteCategory,nil];
return categories;
}
```
As you can see in the example Accept, Decline, and May Be actions are grouped to a category i.e,"INVITE\_CATEGORY". For declaring categories for iOS8 and iOS9 use **MONotificationCategory** from our SDK.
**Notification Categories**
MoEngage recommended not to change the actions grouped in a category across the app versions, as it will lead to users seeing different actions for the same category across different app versions.
# Tracking User Actions
## FOR iOS10 and Above :
As mentioned earlier, use the delegate methods of **UNUserNotificationCenter** to handle actions of notifications and also call ***userNotificationCenter:didReceiveNotificationResponse:*** of the SDK, to track the actions performed on notifications.
```swift Swift theme={null}
func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void) {
MoEngage.sharedInstance().userNotificationCenter(center, didReceive: response)
//---
completionHandler()
}
```
```objectivec Objective-C theme={null}
- (void)userNotificationCenter:(UNUserNotificationCenter *)center
didReceiveNotificationResponse:(UNNotificationResponse *)response
withCompletionHandler:(void (^)())completionHandler{
[[MoEngage sharedInstance] userNotificationCenter:center didReceiveNotificationResponse:response];
//---
completionHandler();
}
```
## FOR iOS8 and iOS9
To track the action performed by the user on actionable notifications call *handleActionWithIdentifier:forRemoteNotification:* in *application:handleActionWithIdentifier:forRemoteNotification:completionHandler:* as shown below :
```swift Swift theme={null}
func application(_ application: UIApplication, handleActionWithIdentifier identifier: String?, forRemoteNotification userInfo: [AnyHashable : Any], completionHandler: @escaping () -> Void) {
//---
if let identifier = identifier {
MoEngage.sharedInstance().handleAction(withIdentifier: identifier, forRemoteNotification: userInfo)
}
}
```
```objectivec Objective-C theme={null}
- (void)application:(UIApplication *)application handleActionWithIdentifier:(NSString *)identifier forRemoteNotification:(NSDictionary *)notification completionHandler:(void (^)()) completionHandler {
----
[[MoEngage sharedInstance] handleActionWithIdentifier:identifier forRemoteNotification:notification]
completionHandler();
}
```
# Location Triggered
Source: https://moengage.com/docs/developer-guide/ios-sdk/integration-with-older-version-of-sdk/push/advanced/location-triggered
Set up geofence-based location-triggered push notifications in your iOS app with the MoEngage SDK.
[](http://cocoapods.org/pods/MoEngageGeofence)
**Important**
* Starting from **iOS 14.0**, Apple has provided user control to choose the level of precision of location to be shared in App. Now because of this **region monitoring(Geofence feature) will not work in cases where the precise location is disabled by the user**. Refer [link](https://developer.apple.com/videos/play/wwdc2020/10660/) for more info.
* Region monitoring is only supported with **Always authorization**. When-in-use authorization doesn't support this feature. Refer [link](https://developer.apple.com/documentation/corelocation/choosing_the_authorization_level_for_location_services) for more info.
* **Dwell** trigger is **not supported in iOS**, hence the SDK supports only Enter and Exit triggers.
# How to enable Location Triggered?
## Required Permissions:
**Region Monitoring(Geofences) requires Always Authorization and Precise location accuracy to be enabled to work**. Therefore make sure that the app is configured to get these permissions and also it's a good practice to let the user know the context in which these permissions are needed, this will also encourage the user to provide these permissions.
# SDK Installation
## Install using CocoaPod
Integrate the MoEngageGeofence framework by adding the dependency in the podfile as show below.
```Ruby theme={null}
pod 'MoEngageGeofence','~>4.2.0'
```
Now run `pod install` to install the framework
## Install using Swift Package Manager
MoEngageGeofence is supported through SPM from SDK version 4.2.0. To integrate use the following git hub url link and set the branch as master or version as 4.2.0 and above [https://github.com/moengage/MoEngage-iOS-Geofence.git](https://github.com/moengage/MoEngage-iOS-Geofence.git)
## Manual Integration
To integrate the `MoEngageGeofence` SDK manually to your project follow this [doc](https://developers.moengage.com/hc/en-us/articles/4404183451412).
MOGeofence has been renamed to MoEngageGeofence from version 4.2.0.Do update the podfile and import statement accordingly.
## Start Geofence Monitoring:
After integrating the MOGeofence module call `startGeofenceMonitoring()` method to initiate the geofence module. This will fetch the geofences around the current location of the user.
```swift Swift theme={null}
MOGeofence.sharedInstance.startGeofenceMonitoring()
```
```objectivec Objective-C theme={null}
[[MOGeofence sharedInstance] startGeofenceMonitoring];
```
Geofence Handler also has callbacks for `didEnterRegion` and `didExitRegion`. You can get these by confirming to the as `MOGeofence.sharedInstance.setGeofenceDelegate(self)`
```swift Swift theme={null}
extension GeofenceViewController: MOGeofenceDelegate {
func geofenceEnterTriggered(withLocationManager locationManager: CLLocationManager?, andRegion region: CLRegion?, forAccountMeta accountMeta: MOAccountMeta) {
print("Geofence Entered")
}
func geofenceExitTriggered(withLocationManager locationManager: CLLocationManager?, andRegion region: CLRegion?, forAccountMeta accountMeta: MOAccountMeta) {
print("Geofence Exited"
}
}
```
```objectivec Objective-C theme={null}
@interface MyViewController ()
---
- (void)geofenceEnterTriggeredWithLocationManager:(CLLocationManager * _Nullable)locationManager andRegion:(CLRegion * _Nullable)region forAccountMeta:(MOAccountMeta * _Nonnull)accountMeta {
NSLog(@"Geofence Entered");
}
- (void)geofenceExitTriggeredWithLocationManager:(CLLocationManager * _Nullable)locationManager andRegion:(CLRegion * _Nullable)region forAccountMeta:(MOAccountMeta * _Nonnull)accountMeta {
NSLog(@"Geofence Exited");
}
```
# Testing Geofencing
First, create a geofencing campaign on your MoEngage dashboard. You can test geofencing in the following ways:
1. On the simulator:
* You can simulate location as shown below.\\
* Simulate the location for which you have created the campaign on the dashboard.\
If you get the respective call back (the delegate methods in MOGeofenceHandler), you are good to go. On simulator, you will not receive push notifications.
2. On the device:
* You can simulate location for real device from the bar above the console as shown below:\\
* You can add a gpx file with the locations configured. A sample gpx file looks like this:
```XML theme={null}
CustomName
```
On the device, once you get the delegate callback for entering or exit in a region, a notification will be sent to the device. This happens instantly, but the notification might take up to 10 minutes.
# Geo Notifications
Once you have received the notification, to identify geo notifications, there is a custom param “cType” = “geo” in the param app\_extra, as shown below:
```json JSON theme={null}
{
"app_extra" = {
cType = geo;
screenData = {
"" = "";
};
screenName = "";
};
aps = {
alert = "exit london- single fence";
badge = 1;
};
moengage = {
cid = "55a628bcf4c4073bb66a368b_GEO:55a628bcf4c4073bb66a368c_ABab1:2015-07-15_14:11:33.704706";
};
}
```
# Push Notification Implementation
Source: https://moengage.com/docs/developer-guide/ios-sdk/integration-with-older-version-of-sdk/push/advanced/push-notification-implementation
Implement push notifications in your iOS app target and notification service extension using MoEngage.
Make sure you have created the APNS certificate and uploaded it to MoEngage dashboard as mentioned in the [APNS Certificate/ PEM file](https://developers.moengage.com/hc/en-us/articles/4403944011028) before testing the push notification.
# App Target Implementation
# Settings Changes
## Capabilities Tab Changes
First, select your **App Target** and select **Capabilities** do the changes as shown in the image below:
**App Group ID Recommendation**
We recommend having a separate App Group ID set for MoEngage with the format `group.{app bundle id}.moengage`. And make sure the same app group id is enabled for all the targets where MoEngage is being used.
1. Turn **ON** App Groups in for your app target and enable one of the App group ids, in case if you don't have an App Group ID then create one. The name of your app group should be `group.{your_bundle_id}.MoEngage`.
2. Turn **ON** Background mode and set/enable **Remote Notification**.
3. Turn **ON** the **Push Notifications**capability for your app.
On enabling **Remote Notification** background mode, we will be able to track uninstalls even for devices where push notification is disabled by the user.
## Adding UserNotifications framework
In the App's Target add **UserNotifications** framework in **Linked Frameworks and Libraries** and set it **Optional**.
# Code Changes in App Target
## Provide the App Group ID to SDK
Provide the App Group ID selected in **Capabilities** in MOSDKConfig instance while initializing the SDK as shown below:
```swift Swift theme={null}
let sdkConfig = MOSDKConfig(withAppID: "MoEngage Workspace ID")
sdkConfig.appGroupID = "App Group ID"
```
```objectivec Objective-C theme={null}
MOSDKConfig* sdkConfig = [[MOSDKConfig alloc] initWithAppID:"MoEngage Workspace ID"];
sdkConfig.appGroupID = @"App Group ID";
```
## AppDelegate swizzling in SDK
**Segment-MoEngage Integration**
Please note that **AppDelegate Swizzling** doesn't work reliably with [Segment Integration](https://developers.moengage.com/hc/en-us/articles/4405093164692)because of delay in initializing the SDK by Segment, therefore make sure to call the MoEngage SDK methods for all Push related callbacks.
AppDelegate Swizzling is used for intercepting the methods of the AppDelegate class in iOS apps. It allows third-party libraries or SDKs to integrate into the app and handle certain system interactions, such as push notifications and deep linking, without requiring manual setup by developers.
**Default behavior**
By default, the MoEngage SDK swizzles the **AppDelegate** Class to get all the callbacks related to Push Notifications, and also we have applied method swizzling for **UserNotificationCenter** delegate methods. This is to ease the integration of the SDK, and this is introduced from the [SDK version 5.0.0](https://developers.moengage.com/hc/en-us/articles/4404198236564-Change-Log#v5-0-0-0-26).
**Disabling AppDelegate Swizzling in the MoEngage SDK**
You should disable AppDelegate Swizzling if you do not want MoEngage SDK to implicitly handle the callbacks. To disable swizzling, add the flag MoEngageAppDelegateProxyEnabled in the app’s Info.plist file and set it to bool value NO.
In the following sections, we have provided the SDK methods to be called when you get callbacks related to push notifications. Most of them will not be needed in case swizzling is enabled; the same will be mentioned in the description.
## Registering for Push notification
Make sure that class, where UserNotificationCenter delegate methods are implemented, should agree to `UNUserNotificationCenterDelegate` , also set the `UNUserNotificationCenterDelegate` after the app launch in `application:DidFinishLaunchingWithOptions:` as shown below: `(In this case AppDelegate is set to be UserNotificationCenter delegate)` :
Call SDK's `registerForRemoteNotificationWithCategories:` to initiate registration of remote notifications as shown below :
```swift Swift theme={null}
MoEngage.sharedInstance().registerForRemoteNotification(withCategories: nil, withUserNotificationCenterDelegate: self)
```
```objectivec Objective-C theme={null}
[[MoEngage sharedInstance] registerForRemoteNotificationWithCategories:nil withUserNotificationCenterDelegate:self];
```
Now after registering for push, the below-given callback methods will be called. **In case you have disabled swizzling,** call the respective MoEngage SDK methods for the callbacks as shown below :
```swift Swift theme={null}
//Remote notification Registration callback methods
func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
//Call only if MoEngageAppDelegateProxyEnabled is NO
MoEngage.sharedInstance().setPushToken(deviceToken)
}
func application(_ application: UIApplication, didFailToRegisterForRemoteNotificationsWithError error: Error) {
//Call only if MoEngageAppDelegateProxyEnabled is NO
MoEngage.sharedInstance().didFailToRegisterForPush()
}
```
```objectivec Objective-C theme={null}
//Remote notification Registration callback methods
- (void)application:(UIApplication*)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData*)deviceToken {
//Call only if MoEngageAppDelegateProxyEnabled is NO
[[MoEngage sharedInstance] setPushToken:deviceToken]
}
-(void)application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)error {
//Call only if MoEngageAppDelegateProxyEnabled is NO
[[MoEngage sharedInstance]didFailToRegisterForPush];
}
//This method is for getting the types of notifications that app may use
-(void)application:(UIApplication *)application didRegisterUserNotificationSettings:(UIUserNotificationSettings *)notificationSettings{
//Call only if MoEngageAppDelegateProxyEnabled is NO
[[MoEngage sharedInstance]didRegisterForUserNotificationSettings:notificationSettings];
}
```
**Notification Actions**
You can send the set of categories(**UNNotificationCategory** for supporting Notification actions. Get more info regarding notification actions [here](https://developers.moengage.com/hc/en-us/articles/4403961980308).
## Callback methods on receiving Push Notification:
Below are the callbacks the app would receive on receiving the push notifications. **In case you have disabled swizzling,** include calls to MoEngage SDK methods on receiving notification callbacks as shown below:
```swift Swift theme={null}
// MARK:- UserNotifications Framework callback method
@available(iOS 10.0, *)
func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse,
withCompletionHandler completionHandler: @escaping () -> Void) {
//Call only if MoEngageAppDelegateProxyEnabled is NO
MoEngage.sharedInstance().userNotificationCenter(center, didReceive: response)
//Custom Handling of notification if Any
let pushDictionary = response.notification.request.content.userInfo
print(pushDictionary)
completionHandler();
}
@available(iOS 10.0, *)
func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification,
withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) {
//This is to only to display Alert and enable notification sound
completionHandler([.sound,.alert])
}
// MARK:- Remote notification received callback method for iOS versions below iOS10
func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable : Any]) {
//Call only if MoEngageAppDelegateProxyEnabled is NO
MoEngage.sharedInstance().didReceieveNotificationinApplication(application, withInfo: userInfo)
}
```
```objectivec Objective-C theme={null}
// UserNotifications Framework Callback for iOS10 and above
- (void)userNotificationCenter:(UNUserNotificationCenter *)center
didReceiveNotificationResponse:(UNNotificationResponse *)response
withCompletionHandler:(void (^)())completionHandler{
//Call only if MoEngageAppDelegateProxyEnabled is NO
[[MoEngage sharedInstance] userNotificationCenter:center didReceiveNotificationResponse:response];
//Custom Handling of notification if Any
completionHandler();
}
- (void)userNotificationCenter:(UNUserNotificationCenter *)center
willPresentNotification:(UNNotification *)notification
withCompletionHandler:(void (^)(UNNotificationPresentationOptions options))completionHandler{
//This is to only to display Alert and enable notification sound
completionHandler((UNNotificationPresentationOptionSound
| UNNotificationPresentationOptionAlert ));
}
//Remote notification received callback method for iOS versions below iOS10
- (void) application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo {
//Call only if MoEngageAppDelegateProxyEnabled is NO
[[MoEngage sharedInstance]didReceieveNotificationinApplication:application withInfo:userInfo];
}
```
Method ***userNotificationCenter:willPresentNotification:withCompletionHandler:*** is called when the app receives notification in foreground. Here, in the completion handler you can mention how you want to let the user know that the app has received a notification.
Method ***userNotificationCenter:didReceiveNotificationResponse:withCompletionHandler*** is called when the app receives a response from the user. Response can be **Default Click** on the Notification or **Dismissing** the notification or any of the other **custom actions** implemented using UNUserNotificationCategory. Here, call ***userNotificationCenter:didReceiveNotificationResponse:*** of MoEngage class.
* ***userNotificationCenter:didReceiveNotificationResponse:withCompletionHandler*** is the only method called when the user clicks on notification, if implemented. Therefore, include your custom handlers here instead of application:didReceiveRemoteNotification: for iOS10.
* While implementing deep links, make sure that you have added the apps URL Scheme to **LSApplicationQueriesSchemes** array in Info.plist to whitelist your app. Without this, the deep links won't work post iOS9.
# Disable Badge Reset
By default, the SDK sets the notification badge count to **0** on every app launch and this also clears the notifications in the device notification center. In case if you would like to keep the notifications even after the App Launch then disable badge reset by calling the below method
```swift Swift theme={null}
MoEngage.sharedInstance().setDisableBadgeReset(true)
```
```objectivec Objective-C theme={null}
[[MoEngage sharedInstance] setDisableBadgeReset:true];
```
# Custom Sound for Notification
You can have a custom tone for notifications of your app. iOS platform supports .aiff , .caf and .wav files for custom Notification tone. For this make sure the sound file of tone is included in your app bundle. Once this is done make sure to provide the sound filename for Notification Sound(In Rich Content Section) while creating the campaign in the dashboard as shown below, and it should work:
# Silent Push Handling
We make use of silent pushes for uninstall tracking(If opted for in the [dashboard settings](https://help.moengage.com/hc/en-us/articles/360043026012-Uninstall-Tracking)). Our system sends silent pushes to the entire user base of the app for the same. The push payload which is sent from MoEngage for silent pushes would look like below:
```json JSON theme={null}
{
"aps" : {
"content-available" : 1
},
"moengage" : {
"silentPush" : 1
}
}
```
Make sure to check for `silentPush` key inside `moengage` and handle the app launches and notification received callbacks in case of these silent pushes.
**Test Silent Push**
For testing the flow with silent pushes, refer to [Uninstall Tracking](/docs/user-guide/settings/analytics/uninstall-tracking).
# Notification Service Extension Target Implementation
# Why add a Notification Service Extension to your project?
Notifications have got a complete revamp after the release of iOS10 with the introduction of new `UserNotifications` and `UserNotificationsUI` framework. And with this we got Notification Service App Extensions, which can be used for following:
1. **Add media support in Notifications:** Post iOS10 Apple has given us the ability to add images, gifs, audio, and video files to the notifications and this can be done using the Notification Service Extension.
2. **For supporting Inbox Feature:** Notification Service Extension is also used to save the received notifications which can later be shown in the App Inbox.
3. **For Updating the Notification Badge count:** MoEngage makes use of the extension to update the notification badge count and doesn't send badge in the notification payload.
4. **For Tracking Notification Impression:** We can track if a Notification is received by the device using the Notification Service Extension.
# Follow the below steps to set up Notification Service Extension:
## 1. Create a Notification Service Extension Target:
Set the name of the extension target and the programing language which you want to use:
After the target is created, Activate the scheme for Extension when prompted for the same. After this, your extension will be added to the project you will see a class with the extension name provided by you while creating and .plist file associated with it.
## 2. Enable Push Notification Capabilities
Then make sure that the Push Notifications Capability is enabled for the Notification Service Extension created:
## 3. Add UserNotifications framework to extension target:
Add `UserNotifications` framework to `Linked Frameworks and Libraries` of notification service extension target as shown below:
## 4. Integrate MoEngageRichNotification framework to Extension:
### Integrate using CocoaPod
For integrating through CocoaPod, include **MoEngageRichNotification** pod for your Notification Service Extension as shown below, and run pod update / install command :
```Ruby theme={null}
target "NotificationServices" do
pod 'MoEngageRichNotification','~>6.2.0'
end
```
### Integrate using Swift Package Manager
MoEngageRichNotification is supported through SPM from SDK version 6.2.0. To integrate use the following github url link and set the branch as master or version as 6.2.0 and above [https://github.com/moengage/MoEngage-iOS-RichNotification.git](https://github.com/moengage/MoEngage-iOS-RichNotification.git)
**Manual Integration**
* To integrate the `MoEngageRichNotification` SDK manually to your project follow this [doc](https://developers.moengage.com/hc/en-us/articles/4404183451412).
* Add `MoEngageRichNotification` to embedded binaries in the App target, and is linked in your Notification Service Extension target.
MORichNotification has been renamed to MoEngageRichNotification from version 6.0.0.Do update the podfile and import statement accordingly.
## 5. Set the App Group ID for Extension:
Turn ON App Groups in for your notification service extension target and enable the same App group id which was selected for the App Target(In the above steps).
## 6. Code Changes in Notification Service Extension:
```swift Swift theme={null}
import UserNotifications
// 1st Step
import MoEngageRichNotification
class NotificationService: UNNotificationServiceExtension {
var contentHandler: ((UNNotificationContent) -> Void)?
var bestAttemptContent: UNMutableNotificationContent?
override func didReceive(_ request: UNNotificationRequest, withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void) {
// 2nd Step
MORichNotification.setAppGroupID()
self.contentHandler = contentHandler
bestAttemptContent = (request.content.mutableCopy() as? UNMutableNotificationContent)
// 3rd Step
MORichNotification.handle(richNotificationRequest: request, withContentHandler: contentHandler)
}
override func serviceExtensionTimeWillExpire() {
// Called just before the extension will be terminated by the system.
// Use this as an opportunity to deliver your "best attempt" at modified content, otherwise the original push payload will be used.
if let contentHandler = contentHandler, let bestAttemptContent = bestAttemptContent {
contentHandler(bestAttemptContent)
}
}
}
```
```objectivec Objective-C theme={null}
#import "NotificationService.h"
// 1st Step
#import
@interface NotificationService ()
@property (nonatomic, strong) void (^contentHandler)(UNNotificationContent *contentToDeliver);
@property (nonatomic, strong) UNMutableNotificationContent *bestAttemptContent;
@end
@implementation NotificationService
- (void)didReceiveNotificationRequest:(UNNotificationRequest *)request withContentHandler:(void (^)(UNNotificationContent * _Nonnull))contentHandler {
@try {
// 2nd Step
[MORichNotification setAppGroupID:];
self.contentHandler = contentHandler;
self.bestAttemptContent = [request.content mutableCopy];
// 3rd Step
[MORichNotification handleWithRichNotificationRequest:request withContentHandler:contentHandler];
} @catch (NSException *exception) {
NSLog(@"MoEngage : exception : %@",exception);
}
}
/// Save the image to disk
- (void)serviceExtensionTimeWillExpire {
// Called just before the extension will be terminated by the system.
// Use this as an opportunity to deliver your "best attempt" at modified content, otherwise the original push payload will be used.
self.contentHandler(self.bestAttemptContent);
}
@end
```
Refer to the code above and do the following changes:
1. Import `MoEngageRichNotification` framework.
2. Set the App Group ID selected in the settings earlier using `setAppGroupID:` method.
3. Call `handleRichNotificationRequest: withContentHandler:` method.
**CriticalRich Notification Media Limitations:**
* Refer to the following [link](https://developer.apple.com/reference/usernotifications/unnotificationattachment) to know about the size and format limitation for attachments(media) supported in Rich Notifications.
* Http URL's aren't supported in iOS10 unless explicitly specified in the plist. You will have include App Transport Security Settings Dictionary in your Notification Service Extension's Info.plist and inside this set Allow Arbitrary Loads to YES.
**Image Guidelines**
* File Size: The maximum file size for image attachments can be 10MB.
* Dimensions: The maximum possible dimensions are 1038 x 1038 pixels. It can be anything smaller than 1038 pixels.
* Landscape vs Portrait: iOS supports both the orientations but we recommend using images that have a landscape orientation this is because depending on the dimensions, portrait images may look too tall.
# Test/Live Builds
* If you are testing the app on **Test Flight or on a live app store build**, make sure you upload the adhoc or production pem to our dashboard. And also in this case you have to send push notifications from **Live environment** of your account.
* For dev build, you can upload development or production certificate in dashboard, but make sure that you create your campaign in **Test environment**, as you cannot send push notifications to dev build from Live environment.
# Notification Payload
An example of the push payload sent to the app:
```json iOS Push Payload theme={null}
{
"aps": {
"alert": {
"title": "Notification Title",
"subtitle": "Notification Subtitle",
"body": "Notification Body"
},
"badge": 1,
"sound": "default",
"category": "INVITE_CATEGORY",
"content-available": 1,
"mutable-content": 1
},
"app_extra": {
"moe_deeplink": "moeapp://screen/settings",
"screenName": "Screen Name",
"screenData": {
"key1": "val1",
"key2": "val2"
}
},
"moengage": {
"silentPush": 1,
"cid": "55f2ba15a4ab4104a287bf88",
"app_id": "DAO6UGZ73D9RTK8B5W96TPYN_DEBUG",
"moe_campaign_id": "55f2ba15a4ab4104a287bf88",
"moe_campaign_name": "Campaign Name",
"inbox_expiry": "1571905058",
"webUrl": "https://google.com",
"couponCode": "APP200",
"media-attachment": "https://image.moengage.com/testImg.png",
"media-type": "image"
}
}
```
Description of different keys in the payload:
* **aps**: This key is used by the iOS to display the notification, and the following are the keys present within it:
* **alert** : Message Content.
* **title** : Gives Notification title.
* **subtitle** : Gives Notification subtitle.
* **body** : Gives the message body of the notification
* **badge**: Gives the badge number to be displayed on top of the App Icon. MoEngage platform supports only two possible values i.e, 0/1. If the value is 1 then the SDK will increment the badge number on the app icon and if it's 0 then the badge number will be reset and there will be no badge displayed on the app icon.
* **sound**: This key gives the filename of the audio file to be played on receiving the notification. If no filename is provided while creating the campaign, to play the os default sound this key is set to the value "default".
* **category**: This key is used by OS for deciding the set of action buttons to be displayed for the notification. Also, the same category is used by OS to decide which Notification Content Extension target to display if present.
* **content-available**: If the value of this key is set to 1, then if the app is present in the background it will get a callback(`application:didReceiveRemoteNotification:fetchCompletionHandle`) to refresh the app content in background. Use this key only if you have to process the push notification in background. By default, this key will be unset.
* **mutable-content**: This key is by default set to 1 for all the campaigns, this is to make sure that the Notification Service Extension target gets the callback on receiving the notification to be processed by MORichNotification. If set to 0 the extension target won't get the callback.
* **app\_extra**: This key will contain the keys which are to be used by App Developers, i.e, Custom key value pairs and screenName for navigation.
* **moe\_deeplink**: This key contains the deeplinking URL if provided during the campaign creation. The SDK will process this key and will attempt to open the deeplink URL if it's valid.
* **screenName**: This key gives screen name where the user has to be navigated on clicking the notification. This navigation is not done by the SDK. The possible values for this parameter are something which app developers will have to define in their project. If provided while creating the campaign, it will be present in the notification payload. And implementing the part to parse and get `screenName` parameter's value and to navigate to the mentioned screen has to be implemented by the app developers.
* **screenData**: This contains the custom key-value pairs entered while creating the campaign, which can be made use by the app developers for any of their use-cases.
* **moengage** : This will contain keys which are to be used by SDK, app developers should not be making any change to this part of the payload and also avoid using this part of the payload, as we may update the structure of this part of payload as per our need. (with the exception being cid, media-attachment, media-type, app\_id which we will not change)
* **silentPush**: This key is present and set to `1` for silent pushes sent from MoEngage.
* **cid**: Unique ID for the campaign.
* **app\_id**: The Workspace ID of the account where the campaign was created.
* **moe\_campain\_id** and **moe\_campaign\_name** : Used by analytics module to track attributes for Notification related events.
* **inbox\_expiry**: This key gives the timestamp at which the notification will be deleted from the app inbox.
* **webUrl**: This key contains the Rich-landing URL if provided during the campaign creation. The SDK will process this key and will open the URL(if valid) in an instance of SFSafariViewController. Use Rich-landing action if you wish to open a web page inside the app on click of the push notification. For e.g. `webUrl` - [https://www.google.com](https://www.google.com/).
* **couponCode**: This key contains the coupon code if provided during the campaign creation. On clicking the notification, if this key is present in the push payload the SDK will display an alert with the coupon code and will give an option to user to copy the coupon to the os clipboard.For e.g. `couponCode` - APP200.
* **media-attachment:** The media-attachment key in the payload gives you the URL of the media which you can download.
* **media-type**: Type of media present in the URL given in media-attachment i.e, image/audio/video.
**Define Valid URL Schemes for DeepLinks (LSApplicationQueriesSchemes)**
**LSApplicationQueriesSchemes**(Array - iOS) Specifies the URL schemes you want the app to be able to use with the `canOpenURL:` method of the UIApplication class(which is being used in our SDK). For each URL scheme you want your app to use with the deeplinks, add it as a string in this array in Info.plist. For more info follow this [link](https://developer.apple.com/library/content/documentation/General/Reference/InfoPlistKeyReference/Articles/LaunchServicesKeys.html#//apple_ref/doc/uid/TP40009250-SW14).
**HTTP URLs**
Http URL's aren't supported in iOS9 unless explicitly specified in the plist. You will have include **App Transport Security Settings** Dictionary in your Info.plist and inside this set **Allow Arbitrary Loads** to **YES**.
# Push Templates
Source: https://moengage.com/docs/developer-guide/ios-sdk/integration-with-older-version-of-sdk/push/advanced/push-templates
Customize expanded push notification appearance using Notification Content Extension with MoEngage.
Starting from MoEngage iOS SDK version `6.2.0` and MORichNotification version `4.0.0`, push templates will be supported where you would be able to customize the way notification looks in expanded mode. This feature is supported in**iOS 12.0 and above**. For info on how to create campaigns with templates in the dashboard refer to this [link](https://help.moengage.com/hc/en-us/articles/4415622460948-Push-Templates?_gl=1*1oma0f1*_ga*MTU4NTM0MzI0NS4xNzI3MTcwMzYy*_ga_SEBHW7YTZ7*czE3NzA2OTUzMDIkbzE0NiRnMSR0MTc3MDcxODYxOSRqNjAkbDAkaDA.).
**iOS 15.0 Update**
With iOS 15.0 update, we have released `MORichNotification` version `5.2.0` where for iOS 15.0 and above we have updated the layout to show content at the top and media at bottom, to have it in line with the standard notification layout.
Make sure you have completed the [App Target](https://developers.moengage.com/hc/en-us/articles/43960004257428-iOS-Push-Integration-Tutorial#h_01K4MBZ6WN3PTSSS6N6K2YQ8S2) and [Notification Service Extension](https://developers.moengage.com/hc/en-us/articles/43960004257428-iOS-Push-Integration-Tutorial#h_01K4MBZ6WN3PTSSS6N6K2YQ8S2) Implementation for supporting Rich Push in your project before proceeding with the below steps.
# STEPS:
For supporting these custom push templates, your project needs to have a Notification Content Extension. Follow the below steps to create a Content Extension and to set it up to support MoEngage templates:
## 1. Create a Notification Content Extension
After the target is created, Activate the scheme for Extension when prompted for the same. After this, your extension will be added to the project you will see a class with the extension name provided by you while creating and .plist file associated with it.
## 2. Set deployment target and Add Required frameworks
Now set the deployment target to **iOS 12.0** or above, since we support this feature from iOS 12.0. After that add `UserNotifications.framework` and `UserNotificationsUI.framework` in Frameworks and Libraries as shown:
## 3. Add required Capabilities
In Capabilities Section add **App Groups** and select the same app group id which you have configured in your App target and Notification Service Extension target.
**App Group ID Recommendation**
We recommend having a separate App Group ID set for MoEngage with the format `group.{app bundle id}.MoEngage`. And make sure the same app group id is enabled for all the targets where MoEngage is being used.
## 4. Info.plist changes
Make the changes in the `Info.plist` of your Notification Content Extension, as shown above, set NSExtensionAttributes as following:
| Attribute | Attribute Value |
| ---------------------------------------------- | ------------------- |
| UNNotificationExtensionCategory | MOE\_PUSH\_TEMPLATE |
| UNNotificationExtensionInitialContentSizeRatio | 1.2 |
| UNNotificationExtensionDefaultContentHidden | YES |
| UNNotificationExtensionUserInteractionEnabled | YES |
## 5. Storyboard changes
Select `MainInterface.storyboard` in your Content extension and remove the default label which is placed there and set the background color of the view to clear color, as shown:
## 6. MoEngageRichNotification Integration
### Integration via CocoaPod
For integrating through CocoaPod, include **MoEngageRichNotification** pod for your Notification Content Extension as shown below, and run pod update / install command :
```Ruby theme={null}
target "PushTemplatesExtension" do
pod 'MoEngageRichNotification','~>6.2.0'
end
```
### Integration via Swift Package Manager
For integrating through SPM, use the following github url link and set the branch as master or version as 6.2.0 and above [https://github.com/moengage/MoEngage-iOS-RichNotification.git](https://github.com/moengage/MoEngage-iOS-RichNotification.git)
**Manual Integration**
* To integrate the `MoEngageRichNotification` SDK manually to your project follow this [doc](https://developers.moengage.com/hc/en-us/articles/4404183451412).
* Add `MoEngageRichNotification` to embedded binaries in the App target, and ensure it is linked to your Notification Content Extension target.
## 7. Code Changes in Content Extension:
```swift Swift theme={null}
import UIKit
import UserNotifications
import UserNotificationsUI
import MoEngageRichNotification
class NotificationViewController: UIViewController, UNNotificationContentExtension {
override func viewDidLoad() {
super.viewDidLoad()
// Set App Group ID
MORichNotification.setAppGroupID("Your App Group ID")
}
func didReceive(_ notification: UNNotification) {
// Method to add template to UI
MORichNotification.addPushTemplate(toController: self, withNotification: notification)
}
}
```
```objectivec Objective-C theme={null}
#import "NotificationViewController.h"
#import
#import
#import
@interface NotificationViewController ()
@end
@implementation NotificationViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Set App Group ID
[MORichNotification setAppGroupID:@"Your App Group ID"];
}
- (void)didReceiveNotification:(UNNotification *)notification {
// Method to add template to UI
[MORichNotification addPushTemplateToController:self withNotification:notification];
}
@end
```
As shown above, make these changes in your `NotificationViewController` class:
1. Set the same App Group ID in `viewDidLoad()` method which was enabled in [Capabilities](https://developers.moengage.com/hc/en-us/articles/4403956104084-Push-Templates#3-add-required-capabilities-0-3). `[Recommended: group.{app bundle id}.MoEngage]`
2. Call `addPushTemplateToController:withNotification:` method to add template in `didReceiveNotification()` callback.
## 8. Notification Click callback in App:
In the case of Simple Image Carousel notification, to know which slide was clicked by the user, make use of `MOMessagingDelegate` to get `notificationClicked(withScreenName: andKVPairs:)` callback to get key-value pairs and screen name if set for the clicked slide. Refer to the example below, here we are registering for the callback in AppDelegate:
```swift Swift theme={null}
class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterDelegate, MOMessagingDelegate{
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// Set the delegate
MOMessaging.sharedInstance.setMessagingDelegate(self)
//Rest of the implementation
}
// Notification Clicked Callback
func notificationClicked(withScreenName screenName: String?, andKVPairs kvPairs: [AnyHashable : Any]?) {
if let screenName = screenName {
print("Navigate to Screen:\(screenName)")
}
if let actionKVPairs = kvPairs {
print("Selected Action KVPair:\(actionKVPairs)")
}
}
// Notification Clicked Callback with Push Payload
func notificationClicked(withScreenName screenName: String?, kvPairs: [AnyHashable : Any]?, andPushPayload userInfo: [AnyHashable : Any]) {
print("Push Payload: \(userInfo)")
if let screenName = screenName {
print("Navigate to Screen:\(screenName)")
}
if let actionKVPairs = kvPairs {
print("Selected Action KVPair:\(actionKVPairs)")
}
}
}
```
```objectivec Objective-C theme={null}
@interface AppDelegate ()
@end
@implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Set the delegate
[[MOMessaging sharedInstance] setMessagingDelegate:self forAppID:@"YOUR_WORKSPACE_ID"];
//Rest of the implementation
}
// Notification Clicked Callback
-(void)notificationClickedWithScreenName:(NSString *)screenName andKVPairs:(NSDictionary *)kvPairs{
if (screenName) {
NSLog(@"Screen Name : %@",screenName);
}
if (kvPairs) {
NSLog(@"KV Pairs : %@",kvPairs);
}
}
// Notification Clicked Callback with Push Payload
-(void)notificationClickedWithScreenName:(NSString *)screenName KVPairs:(NSDictionary *)kvPairs andPushPayload:(NSDictionary *)userInfo{
NSLog(@"Push Payload: %@",userInfo);
if (screenName) {
NSLog(@"Screen Name : %@",screenName);
}
if (kvPairs) {
NSLog(@"KV Pairs : %@",kvPairs);
}
}
@end
```
This callback will also be called for normal and Stylized Basic Notifications and could be made use of there as well.
# Real-Time Triggers
Source: https://moengage.com/docs/developer-guide/ios-sdk/integration-with-older-version-of-sdk/push/advanced/real-time-triggers
Set up device-triggered push notifications that fire instantly when a user performs an event on iOS.
Real-time device triggers are push notifications that are triggered instantly in the device whenever a trigger event(configured while creating the campaign) is tracked with the SDK [trackEvent:](https://developers.moengage.com/hc/en-us/articles/4403922636308) method. In this case, the notifications are triggered in the device, which enables you to post notifications even in offline scenarios.
Real-Time Triggers are available from SDK version [4.0.0](https://developers.moengage.com/hc/en-us/articles/4404198236564-Change-Log#v4-0-0-0-33)
# SDK Installation
From MoEngage-iOS-SDK version 8.2.0, MoEngageRealTimeTrigger module is separated from the SDK to a separate module as MoEngageRealTimeTrigger and hence has to be added separately.
[](http://cocoapods.org/pods/MoEngageRealTimeTrigger)
## Install using CocoaPod
Integrate the RealTimeTrigger framework by adding the dependency in the podfile as shown below.
```auto Ruby theme={null}
pod 'MoEngageRealTimeTrigger','~>1.2.0'
```
Now run `pod install` to install the framework
## Install using Swift Package Manager
MoEngageRealTimeTrigger is supported through SPM from SDK version 1.2.0. To integrate, use the following github url link and set the branch as master or version as 1.2.0 and above [https://github.com/moengage/MoEngage-iOS-RealTimeTrigger.git](https://github.com/moengage/MoEngage-iOS-RealTimeTrigger.git)
# Manual Syncing
MoEngage SDK syncs all the real-time trigger campaigns whenever the app is **launched OR comes to the foreground**. But in case its needed to manually sync the device triggers for any of the background tasks, use `syncRealTimeTriggersWithCompletionBlock:` as shown below:
```swift Swift theme={null}
MORealTimeTrigger.sharedInstance.syncRealTimeTriggers { (rtSyncCompleted) in
if(rtSyncCompleted){
print("Real-Time trigger sync successfull")
}
}
```
```objectivec Objective-C theme={null}
[[MORealTimeTrigger sharedInstance] syncRealTimeTriggersForAppID:@"YOUR_WORKSPACE_ID" andCompletionHandler:^(BOOL rtSyncCompleted) {
if (rtSyncCompleted) {
NSLog(@"Real-Time trigger sync successfull");
}
}];
```
# Additional Callbacks for version below iOS 10
For iOS 10 and above, MoEngage SDK uses the UserNotification framework for triggering notifications and relies on the callbacks from **UNUserNotificationCenter** to obtain and process the notification payload. Therefore, make sure the SDK methods are called in UNUserNotificationCenter delegate callbacks as mentioned in this [doc](https://developers.moengage.com/hc/en-us/articles/43960004257428-iOS-Push-Integration-Tutorial#h_01K45FKD0XDH4X776R47JA04GJ).
For iOS versions below iOS 10 call `didReceieveNotificationinApplication: withInfo:openDeeplinkUrlAutomatically:` method of MoEngage SDK as shown below.
```swift Swift theme={null}
func application(_ application: UIApplication, didReceive notification: UILocalNotification) {
if let userInfo = notification.userInfo {
MoEngage.sharedInstance().didReceieveNotificationinApplication(application, withInfo: userInfo, openDeeplinkUrlAutomatically: true)
}
}
```
```objectivec Objective-C theme={null}
-(void)application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification *)notification{
NSDictionary* userInfo = notification.userInfo;
[[MoEngage sharedInstance]didReceieveNotificationinApplication:application withInfo:userInfo openDeeplinkUrlAutomatically:YES];
}
```
# Manual Integration
Source: https://moengage.com/docs/developer-guide/ios-sdk/manual-integration/manual-integration
Install and configure MoEngage iOS SDK frameworks manually without using a dependency manager.
To install the frameworks manually, follow the steps below:
Download the latest SDKs from our GitHub repository. You can find the download URLs for each SDK in [package.json](https://github.com/moengage/apple-sdk/blob/master/package.json).
# Embedded Frameworks in App Target
Make sure to embed the required frameworks to App Target as described in the following image, set the Embed option to **Embed & Sign** for MoEngage framework files:
Required Frameworks:
| Frameworks | Status | Purpose |
| --------------------- | -------- | --------------------------------------------------------------------------------------------- |
| MoEngageCore | Required | Provides the foundational services, initialization, and lifecycle management for the SDK. |
| MoEngageSDK | Required | Serves as the primary public interface and orchestrator for all MoEngage SDK functionalities. |
| MoEngageSecurity | Required | Enables data protection during access and transmission. |
| MoEngageMessaging | Required | Manages push notification registration, payload handling, and core messaging features. |
| MoEngageCampaignsCore | Required | Manages campaign payload handling and display. |
Add-on Frameworks:
| Frameworks | Status | Purpose |
| ------------------------ | -------- | ---------------------------------------------------------------------------------------------------------------- |
| MoEngageTriggerEvaluator | Optional | Enables the display of in-app messages and device triggered push notifications, based on AND trigger conditions. |
| MoEngageInApps | Optional | Enables the display of in-app messages, such as pop-ups and modals. |
| MoEngageGeofence | Optional | Enables geofence monitoring for location-based campaign triggers. |
| MoEngageInbox | Optional | Enables a persistent notification center (inbox) within the application. |
| MoEngageCards | Optional | Enables the display and management of Content Cards. |
| MoEngageRichNotification | Optional | Enables rich media content (images, video, audio) and templates in push notifications. |
| MoEngageRealTimeTrigger | Optional | Facilitates high-frequency, real-time campaign triggers based on user events. |
| MoEngageLiveActivity | Optional | Manages live activities registration, payload handling, and tracking. |
# Link MoEngageRichNotification framework in App Extensions
This is only required if you are using the `MoEnagageRichNotification` framework in the project. Make sure to link the framework in the Extension targets as shown below and set the **Embed** option to **Do Not Embed** in this case, as it is already embedded in your App Target:
In extension target build settings, add `@executable_path/../../Frameworks` as additional `LD_RUNPATH_SEARCH_PATHS`.
# Migration Of MoEngage SDK From Cocoapods To SPM
Source: https://moengage.com/docs/developer-guide/ios-sdk/migration/migration-of-moengage-sdk-from-cocoapods-to-spm
CocoaPods is being deprecated. MoEngage recommends using Swift Package Manager (SPM) for all new integrations.
## Step 1: Remove CocoaPods
To remove the existing CocoaPods integration, clean your configuration files first:
1. Open the `Podfile` in a text editor and remove the lines matching: `pod 'MoEngage-iOS-SDK'`.
2. Open your terminal, navigate to the project's root directory, and execute the following command to update the workspace:
```bash Bash wrap theme={null}
pod install
```
## Step 2: Add Swift Package Manager
Now that the CocoaPods dependency is removed, you transition to the Xcode application to configure the new package.
To install the MoEngage-iOS-SDK through SPM, refer to the [SPM Integration Guide](https://www.moengage.com/docs/developer-guide/ios-sdk/sdk-integration/basic/sdk-integration#integration-through-swift-package-manager).
Include the equivalent Swift package products for the MoEngage features being used by your app.
The MoEngage-iOS-SDK package is now installed.
## Post-migration Verification
Confirm the successful integration of the package:
1. Open the project workspace in Xcode.
2. Check the Project Navigator to ensure the MoEngage-iOS-SDK package appears correctly.
3. Build and run the project to confirm the application compiles without errors.
# Migration to SDK version 6.0.0
Source: https://moengage.com/docs/developer-guide/ios-sdk/migration/migration-to-sdk-version-6-0-0
Migrate your MoEngage iOS SDK integration to version 6.0.0 with updated initialization and APIs.
We have made quite a few changes in SDK version 6.0.0 and if you are someone who was using an older version of our SDK and planning to move to version 6.0.0 or above here are the changes to be made while migrating:
# iOS 8.\* Support Removed
From SDK version `6.0.0` we have set the deployment target as `iOS 9.0` and hence have removed support for `iOS 8.*`. We have decided to do this since the user base in iOS 8.0 is very less and most of the developers are already setting their app's deployment target to greater than iOS 9.0.
# Initialization Method Deprecation
We have deprecated the previous initialization methods and have introduced new methods. This is to simplify initialization by reducing the number of arguments in the method:
**Deprecated methods**
```objective-c Objective C wrap theme={null}
/**
For TEST Environment
@warning This method is deprecated and will be removed from SDK Version 7.0.0. Use initializeDevWithAppID:withLaunchOptions instead
*/
-(void)initializeDevWithApiKey:(NSString *_Nonnull)apiKey inApplication:(UIApplication *_Nullable)application withLaunchOptions:(NSDictionary *_Nullable)launchOptions openDeeplinkUrlAutomatically:(BOOL)openUrl __deprecated_msg("Use initializeDevWithAppID:withLaunchOptions instead.");
/**
For LIVE Environment
@warning This method is deprecated and will be removed from SDK Version 7.0.0. Use initializeProdWithAppID:withLaunchOptions: instead
*/
-(void)initializeProdWithApiKey:(NSString *_Nonnull)apiKey inApplication:(UIApplication *_Nullable)application withLaunchOptions:(NSDictionary *_Nullable)launchOptions openDeeplinkUrlAutomatically:(BOOL)openUrl __deprecated_msg("Use initializeProdWithAppID:withLaunchOptions: instead.");
```
**New methods**
```objective-c Objective C wrap theme={null}
// For TEST Environment
-(void)initializeDevWithAppID:(NSString *_Nonnull)appID withLaunchOptions:(NSDictionary *_Nullable)launchOptions;
// For LIVE Environment
-(void)initializeProdWithAppID:(NSString *_Nonnull)appID withLaunchOptions:(NSDictionary *_Nullable)launchOptions;
```
For more info on SDK Initialization, refer to the following [doc](/docs/developer-guide/ios-sdk/sdk-integration/basic/sdk-initialization).
# Analytics Module Changes
## Track Event:
We have Introduced `MOProperties` class to track all the event attributes. And hence we have deprecated the earlier methods and included `trackEvent:withProperties:` to track events. Refer to this [link](/docs/developer-guide/ios-sdk/data-tracking/basic/tracking-events) for more info.
**Deprecated Methods**
```objective-c Objective C wrap theme={null}
/**
@warning This method is deprecated and will be removed in SDK version 7.0.0. Use trackEvent:withProperties: instead.
*/
-(void)trackEvent:(NSString *_Nonnull)name andPayload:(NSMutableDictionary *_Nullable)payload __deprecated_msg("Use trackEvent:withProperties: instead.");
/**
@warning This method is deprecated and will be removed in SDK version 7.0.0. Use trackEvent:withProperties: instead.
*/
-(void)trackEvent:(NSString *_Nonnull)name builderPayload:(MOPayloadBuilder *_Nullable)payload __deprecated_msg("Use trackEvent:withProperties: instead.");
```
**New methods**
```objective-c Objective C wrap theme={null}
/**
Call this method to track events.
@param name Event name to be tracked
@param properties of type MOProperties. See MOProperties for more details.
@version Available from SDK version 6.0.0 and above
*/
-(void)trackEvent:(NSString *_Nonnull)name withProperties:(MOProperties *_Nullable)properties;
```
## User Attribute Tracking:
We have added a couple of additional methods to track date user attributes in the SDK, more info in this [doc](/docs/developer-guide/ios-sdk/data-tracking/basic/tracking-user-attributes) :
```objective-c Objective C wrap theme={null}
/**
Use this method to set a user attribute which is NSDate instance.
@param date The NSDate instance value to track.
*/
-(void)setUserAttributeDate:(NSDate* _Nonnull)date forKey:(NSString *_Nonnull)key;
/**
Use this method to set a user attribute which is NSDate instance.
@param dateStr Date String in ISO date format [yyyy-MM-dd'T'HH:mm:ss'Z'].
*/
-(void)setUserAttributeISODateString:(NSString* _Nonnull)dateStr forKey:(NSString *_Nonnull)key;
```
# Messaging Module Changes
## Notification Received Method Deprecation:
**AppDelegate Swizzling :**
From SDK version 5.0.0, we have implemented Swizzling of AppDelegate inside the SDK, due to which SDK, by default, gets all the callbacks related to Notifications. Therefore, if AppDelegate Swizzling is enabled, then developers can skip calling the SDK methods on Notification Callbacks. For more info on the same, refer to the following [link](/docs/developer-guide/ios-sdk/push/basic/ios-push-integration-tutorial#verify-dashboard-configuration).
We have deprecated `didReceieveNotificationinApplication:withInfo:openDeeplinkUrlAutomatically:` instead, use `didReceieveNotificationinApplication:withInfo:`.
**Deprecated method**
```objective-c Objective C wrap theme={null}
/**
Call this method in AppDelegate in didReceiveRemoteNotification
@warning This method is deprecated and will be removed from SDK Version 7.0.0. Use didReceieveNotificationinApplication:withInfo: instead
*/
-(void)didReceieveNotificationinApplication:(UIApplication*_Nullable)application withInfo:(NSDictionary* _Nonnull)userInfo openDeeplinkUrlAutomatically:(BOOL)openUrl __deprecated_msg("Use didReceieveNotificationinApplication:withInfo: instead.");
```
**New methods**
```objective-c Objective C wrap theme={null}
/**
Call this method in AppDelegate in didReceiveRemoteNotification
*/
-(void)didReceieveNotificationinApplication:(UIApplication*_Nullable)application withInfo:(NSDictionary* _Nonnull)userInfo;
```
# InApp Module Changes
* The InApp module has been completely revamped and includes a lot of changes, the primary one being we have separated the module from the `MoEngage-iOS-SDK`. Therefore, now to use In-Apps in your app you will have to integrate `MoEngageInApp` module to your project. Refer to this [link](/docs/developer-guide/ios-sdk/in-app-messages/in-app-nativ) for integrating the inApp Module.
* We have **removed support** for InApps in **Landscape Orientation** and **iPads** as the existing templates that we have are not made considering them, and therefore may end up breaking in the said scenarios.
* We have added support for **Context-based InApps** wherein we can tag InApps and show InApps based on the current context set on the SDK. Refer to this [link](/docs/developer-guide/ios-sdk/in-app-messages/in-app-nativ) for more info.
* We have removed support for most of the older methods and replaced them with new ones to have it consistent across platforms. To know the differences in the API to be used refer below links:
* [InApp APIs for SDK version 6.0.0 and above](/docs/developer-guide/ios-sdk/in-app-messages/in-app-nativ)
* [InApp APIs for SDK version 5.2.6 and below](https://developers.moengage.com/hc/en-us/articles/4404155414676)
# Migration to SDK version 7.0.0
Source: https://moengage.com/docs/developer-guide/ios-sdk/migration/migration-to-sdk-version-7-0-0
Migrate your MoEngage iOS SDK integration to version 7.0.0 with updated initialization and config.
We have made quite a few changes in SDK version 7.0.0 and if you are someone who was using an older version of our SDK and planning to move to version 7.0.0 or above here are the changes to be made while migrating:
# iOS 9.\* Support Removed
From SDK version `7.0.0` we have set the deployment target as `iOS 10.0` and hence have removed support for `iOS 9.*`. We have decided to do this since the user base in iOS 9.0 is very less and most of the developers are already setting their app's deployment target to greater than iOS 10.0.
# Initialisation Method Changes
We have deprecated the previous initialization methods and have introduced new methods. The new methods accept the MOSDKConfig instance as an argument which can contain multiple parameters along with the Workspace ID required for setting up the SDK on app launch:
**Deprecated methods**
```swift Swift wrap theme={null}
var yourMoEAppID = "YOUR_WORKSPACE_ID"
#if DEBUG
MoEngage.sharedInstance().initializeDev(withAppID:yourMoEAppID, withLaunchOptions: nil)
#else
MoEngage.sharedInstance().initializeProd(withAppID:yourMoEAppID, withLaunchOptions: nil)
#endif
```
```objective-c Objective C wrap theme={null}
NSString* yourMoEngageAppID = @"YOUR_WORKSPACE_ID";
#ifdef DEBUG
[[MoEngage sharedInstance] initializeDevWithAppID:yourMoEngageAppID withLaunchOptions:launchOptions];
#else
[[MoEngage sharedInstance] initializeProdWithAppID:yourMoEngageAppID withLaunchOptions:launchOptions];
#endif
```
For info on the new initialization methods, refer to this [link](/docs/developer-guide/ios-sdk/sdk-integration/basic/sdk-initialization).
# Setting Region/Data Center
MODataCenter: We’ve introduced new APIs to set up the Data center in the SDK, earlier API which made use of `DataRedirectionRegion` Enumerator is no longer supported, instead the same has to be configured using MOSDKConfig and provided during Initialization, refer [doc](/docs/developer-guide/ios-sdk/sdk-integration/basic/data-center) for info on how to configure in the latest SDK versions.
**Older method \[6.3.1 and below]**
```swift Swift wrap theme={null}
/*
DataRedirectionRegion Enum Values:
MOE_REGION_SERV3
MOE_REGION_EU
MOE_REGION_DEFAULT
*/
// Eg to redirect data to EU Data Center
MoEngage.redirectData(to: MOE_REGION_EU)
```
```objective-c Objective C wrap theme={null}
/*
DataRedirectionRegion Enum Values:
MOE_REGION_SERV3
MOE_REGION_EU
MOE_REGION_DEFAULT
*/
// Eg to redirect data to EU Data Center
[MoEngage redirectDataToRegion:MOE_REGION_EU];
```
# App Target: Setting App Group ID
App Group ID for App target was earlier set using `setAppGroupID:`, which is no longer supported. Make use of the MOSDKConfig to configure the same in the latest SDK versions (Refer [link](/docs/developer-guide/ios-sdk/push/basic/ios-push-integration-tutorial#step-1-implement-notification-service-extension-nse) for more info)
**Older method \[6.3.1 and below]**
```swift Swift theme={null}
MoEngage.setAppGroupID("AppGroupID")
```
```objective-c Objective C wrap theme={null}
[MoEngage setAppGroupID:@"AppGroupID"];
```
# Enable Logs Changes
For troubleshooting, the method to enable SDK logs has been changed from `debug:` to `enableSDKLogs:`. The earlier method was used as shown below, and for the updated implementation in the latest SDK versions refer to the following [doc](/docs/developer-guide/ios-sdk/troubleshooting-and-faqs/troubleshooting-and-faqs-ios#enable-sdk-logs):
**Older method \[6.3.1 and below]**
```swift Swift theme={null}
MoEngage.debug(LOG_ALL)
```
```objective-c Objective C wrap theme={null}
[MoEngage debug:LOG_ALL];
```
# Migration to SDK version 8.2.0
Source: https://moengage.com/docs/developer-guide/ios-sdk/migration/migration-to-sdk-version-8-2-0
Migrate your MoEngage iOS SDK to version 8.2.0 with updated pod names, imports, and initialization.
We have made some major changes in SDK version 8.2.0, and if you are someone who was using an older version of our SDK and planning to move to version 8.2.0 or above, here are the changes to be made while migrating:
# Update the Pod names
From SDK version `8.2.0` , we have updated some of the pod names. Refer to the table below to update the podfile.
| SDK Version 7.0.0 | SDK Version 8.2.0 |
| :------------------------ | :------------------------------ |
| import MOGeofence | import MoEngageGeofence |
| import MORichNotification | import MoEngageRichNotification |
# Update the Import
From SDK version `8.2.0` , we have updated some of the framework names. Refer to the table below to update the import statement for the modules.
| SDK Version 7.0.0 | SDK Version 8.2.0 |
| :------------------------ | :------------------------------ |
| import MoEngage | import MoEngageSDK |
| import MOInApp | import MoEngageInApps |
| import MOCards | import MoEngageCards |
| import MOGeofence | import MoEngageGeofence |
| import MORichNotification | import MoEngageRichNotification |
# Initialisation Method Changes
We have deprecated the previous initialization methods and have introduced new methods.
**Deprecated methods**
```swift Swift wrap theme={null}
let sdkConfig = MOSDKConfig(withAppID: "YOUR_WORKSPACE_ID")
#if DEBUG
MoEngage.sharedInstance().initializeTest(with: sdkConfig, andLaunchOptions: launchOptions)
#else
MoEngage.sharedInstance().initializeLive(with: sdkConfig, andLaunchOptions: launchOptions)
#endif
```
```objective-c Objective C wrap theme={null}
MOSDKConfig* sdkConfig = [[MOSDKConfig alloc] initWithAppID:@"YOUR_WORKSPACE_ID"];
#ifdef DEBUG
[[MoEngage sharedInstance] initializeTestWithConfig:sdkConfig andLaunchOptions:launchOptions];
#else
[[MoEngage sharedInstance] initializeLiveWithConfig:sdkConfig andLaunchOptions:launchOptions];
#endif
```
For info on the new initialization methods, refer to this [link](/docs/developer-guide/ios-sdk/sdk-integration/basic/sdk-initialization).
# Setting Region/Data Center
MODataCenter: DataCenter names have been updated in the SDK version `8.2.0`. Following are the existing ones used in the SDK version in `7.0.0` and above.
```swift Swift theme={null}
typedef enum {
DATA_CENTER_01,
DATA_CENTER_02,
DATA_CENTER_03
}MODataCenter;
```
```objective-c Objective C theme={null}
typedef enum {
DATA_CENTER_01,
DATA_CENTER_02,
DATA_CENTER_03
}MODataCenter;
```
For info on the new DataCenter value, refer to this [link](/docs/developer-guide/ios-sdk/sdk-integration/basic/data-center).
# App Target: Setting App Group ID
App Group ID for App target was earlier set using `setAppGroupID:`, which is no longer supported. Make use of the MOSDKConfig to configure the same in the latest SDK versions(Refer to the [link](/docs/developer-guide/ios-sdk/sdk-integration/basic/sdk-initialization) for more info)
**Older method\[6.3.1 and below]**
```swift Swift theme={null}
MoEngage.setAppGroupID("AppGroupID")
```
```objective-c Objective C theme={null}
[MoEngage setAppGroupID:@"AppGroupID"];
```
# Enable Logs Changes
For troubleshooting, the method to enable SDK logs has been changed from `debug:` to `enableSDKLogs :`. The earlier method was used, as shown below, and for the updated implementation in the latest SDK versions, refer to the following [doc](/docs/developer-guide/ios-sdk/troubleshooting-and-faqs/troubleshooting-and-faqs-ios):
**Older method\[6.3.1 and below]**
```swift Swift theme={null}
MoEngage.debug(LOG_ALL)
```
```objective-c Objective C theme={null}
[MoEngage debug:LOG_ALL];
```
# Tracking User Attributes
From SDK version `8.2.0` , we have deprecated a couple of UserAttribute methods. Refer to the table below to get the updated method
| SDK Version 7.0.0 | SDK Version 8.2.0 |
| :------------------------------------------------------------------ | :---------------------------------------------------------------------------------------------------------------------------------------------- |
| MoEngage.sharedInstance().setUserUniqueID(UNIQUE\_ID) | MOAnalytics.sharedInstance.setUniqueID(UNIQUE\_ID) |
| MoEngage.sharedInstance().setUserName(userName) | MOAnalytics.sharedInstance.setName(userName) |
| MoEngage.sharedInstance().setUserLastName(userLastname) | MOAnalytics.sharedInstance.setLastName(userLastname) |
| MoEngage.sharedInstance().setUserFirstName(userFirstName) | MOAnalytics.sharedInstance.setFirstName(userFirstName) |
| MoEngage.sharedInstance().setUserEmailID(userEmailID) | MOAnalytics.sharedInstance.setEmailID(userEmailID) |
| MoEngage.sharedInstance().setUserMobileNo(userPhoneNo) | MOAnalytics.sharedInstance.setMobileNumber(userPhoneNo) |
| MoEngage.sharedInstance().setUserGender(MALE) | MOAnalytics.sharedInstance.setGender(.male) |
| MoEngage.sharedInstance().setUserDateOfBirth(userBirthdate) | MOAnalytics.sharedInstance.setDateOfBirth(userBirthdate) |
| MoEngage.sharedInstance().setUserLocationLatitude | MOAnalytics.sharedInstance.setLocation(MOGeoLocation(withLatitude: userLocationLat, andLongitude: userLocationLng)) |
| (userLocationLat, andLongitude: userLocationLng) | |
| MoEngage.sharedInstance().setUserAttributeISODateString | MOAnalytics.sharedInstance.setUserAttributeISODate("2020-01-12T18:45:59Z", withAttributeName: "Date Attr 2")e |
| ("2020-01-12T18:45:59Z", forKey: "DateAttr2") | |
| MoEngage.sharedInstance().setUserAttributeTimestamp | MOAnalytics.sharedInstance.setUserAttributeEpochTime(663333, withAttributeName: "Date Attr 3") |
| (NSDate().timeIntervalSince1970, forKey:"DateAttr3") | |
| MoEngage.sharedInstance().setUserAttributeLocationLatitude | MOAnalytics.sharedInstance.setLocation(MOGeoLocation.init(withLatitude: 72.90909, andLongitude: 12.34567), withAttributeName: "attribute name") |
| (12.98798, longitude: 34.98789, forKey:"location\_attribute\_name") | |
For more information on tracking user attributes methods, refer to this [doc](/docs/developer-guide/ios-sdk/data-tracking/basic/tracking-user-attributes).
# Push Notification
From SDK version `8.2.0` , we have updated methods related to Push Notification.Refer to the table below to use the updated one.
| SDK Version 7.0.0 | SDK Version 8.2.0 |
| :----------------------------------------------------------------------------------- | :---------------------------------------------------------------------------------------------- |
| MoEngage.sharedInstance().disableBadgeReset = true | MoEngage.sharedInstance().setDisableBadgeReset(true) |
| MORichNotification.handle(request, withContentHandler: contentHandler) | MORichNotification.handle(richNotificationRequest: request, withContentHandler: contentHandler) |
| MOPushTemplateHandler.sharedInstance().addPushTemplate(to: self, with: notification) | MORichNotification.addPushTemplate(toController: self, withNotification: notification) |
| //Set messaging delegate | //Set messaging delegate |
| MOMessaging.sharedInstance().messagingDelegate = self | MOMessaging.sharedInstance.setMessagingDelegate(self) |
# RealTimeTrigger and Inbox Module
From SDK version 8.0.0, RealTimeTrigger and Inbox Module are separated from the MoEngage-iOS-SDK.Hence, they must be integrated explicitly. Refer to the [link](/docs/developer-guide/ios-sdk/push/advanced) for the document.
# Location Triggered
From SDK version `8.2.0` , we have updated methods related to Geofence. Refer to the table below to use the updated one.
| SDK Version 7.0.0 | SDK Version 8.2.0 |
| :------------------------------------------------------------ | :-------------------------------------------------- |
| MOGeofenceHandler.sharedInstance()?.startGeofenceMonitoring() | MOGeofence.sharedInstance.startGeofenceMonitoring() |
| MOGeofenceHandler.sharedInstance().delegate = self | MOGeofence.sharedInstance.setGeofenceDelegate(self) |
For more info on the Geofence feature, refer to this [doc](/docs/developer-guide/ios-sdk/push/optional/location-triggered).
# InApp
From SDK version `8.2.0` , we have updated methods related to InApp.Refer to the table below to use the updated one.
| SDK Version 7.0.0 | SDK Version 8.2.0 |
| :--------------------------------------------- | :---------------------------------------------- |
| MOInApp.sharedInstance().show() | MOInApp.sharedInstance().showCampaign() |
| MOInApp.sharedInstance().inAppDelegate = self; | MOInApp.sharedInstance().setInAppDelegate(self) |
For more info on the InApp feature, refer to this [doc](/docs/developer-guide/ios-sdk/in-app-messages/in-app-nativ).
# Cards
From SDK version `8.0.0` , we have updated methods related to Cards. Refer to the table below to use the updated one.
| SDK Version 7.0.0 | SDK Version 8.2.0 |
| :-------------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| MoEngageCards.sharedInstance.pushCardsViewController | MOCards.sharedInstance.pushCardsViewController |
| (toNavigationController: self.navigationController!) | (toNavigationController: self.navigationController!) |
| MoEngageCards.sharedInstance.presentCardsViewController() | MOCards.sharedInstance.presentCardsViewController() |
| MoEngageCards.sharedInstance.getCardsViewController() | MOCards.sharedInstance.getCardsViewController(withUIConfiguration: nil, withCardsViewControllerDelegate: self, forAppID: "YOUR\_WORKSPACE\_ID") cardsController in print("fetched CardsController") |
| MoEngageCards.sharedInstance.getNewCardsCount() | MOCards.sharedInstance.getNewCardsCount(forAppID: "YOUR\_WORKSPACE\_ID", withCompletionBlock: count, accountMeta in print("Card count is (count)") ) |
| MoEngageCards.sharedInstance.getUnclickedCardsCount() | MOCards.sharedInstance.getUnclickedCardsCount(forAppID: "YOUR\_WORKSPACE\_ID") count, accountMeta in print("UnClicked Card count is (count)") |
| MoEngageCards.sharedInstance.cardsDelegate = delegate | MOCards.sharedInstance.setCardsDelegate(delegate: self) |
For more info on the Cards feature, refer to this [doc](/docs/developer-guide/ios-sdk/cards/cards-in-i-os).
# Migration to SDK version 9.0.0
Source: https://moengage.com/docs/developer-guide/ios-sdk/migration/migration-to-sdk-version-9-0-0
Migrate your MoEngage iOS SDK to version 9.0.0 with updated class names and initialization methods.
We have made some class name updates in SDK version 9.0.0 and if you are someone who was using an 8.2.0 and above version of our SDK and planning to move to version 9.0.0 or above, here are the changes to be made while migrating.
# Initialization Method Changes
We have updated the initialization method of SDK with SDKConfig as the required parameter and sdkState as an optional parameter.
### Deprecated methods
```swift file name wrap theme={null}
let sdkConfig = MOSDKConfig(withAppID: "YOUR_WORKSPACE_ID")
#if DEBUG
MoEngage.sharedInstance().initializeDefaultTestInstance(with: sdkConfig, andLaunchOptions: launchOptions)
#else
MoEngage.sharedInstance().initializeDefaultLiveInstance(with: sdkConfig, andLaunchOptions: launchOptions)
#endif
```
```objective-c Objective C wrap theme={null}
MOSDKConfig* sdkConfig = [[MOSDKConfig alloc] initWithAppID:@"YOUR_WORKSPACE_ID"];
#ifdef DEBUG
[[MoEngage sharedInstance] initializeDefaultTestInstanceWithConfig:sdkConfig andLaunchOptions:launchOptions];
#else
[[MoEngage sharedInstance] initializeDefaultLiveInstanceWithConfig:sdkConfig andLaunchOptions:launchOptions];
#endif
```
For information on the new initialization methods, refer to this [link](/docs/developer-guide/ios-sdk/sdk-integration/basic/sdk-initialization).
# Setting Region/Data Center
MoEngageDataCenter: DataCenter class name has been updated in the SDK version `9.0.0`. Following are the existing ones used in the SDK version in `8.2.0` and above.
| SDK Version 8.2.0 | SDK Version 9.0.0 |
| :---------------- | :----------------- |
| MODataCenter | MoEngageDataCenter |
For information on the new DataCenter value, refer to this [link](/docs/developer-guide/ios-sdk/sdk-integration/basic/data-center).
# Install/Update differentiation
| SDK Version 8.x.x | SDK Version 9.0.0 |
| :-------------------------------------------- | :------------------------------------------------------ |
| MoEngage.sharedInstance().appStatus(.install) | MoEngageSDKAnalytics.sharedInstance.appStatus(.install) |
For information on the new DataCenter value, refer to this [document](/docs/developer-guide/ios-sdk/data-tracking/basic/install-update-differentiation).
# Analytics:
From SDK version `9.0.0`, we have updated methods related to Analytics. Refer to the table below to use the updated one.
| SDK Version 8.x.x | SDK Version 9.0.0 |
| :---------------- | :------------------- |
| MOAnalytics | MoEngageSDKAnalytics |
For information on tracking user attributes methods, refer to this [document](/docs/developer-guide/ios-sdk/data-tracking/basic/tracking-events).
# Inapps
From SDK version `9.0.0`, we have updated methods related to Inapps. Refer to the table below to use the updated one.
| SDK Version 8.x.x | SDK Version 9.0.0 |
| :--------------------------------------------------------------------------------------------------------------------------------------------------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| MOInapp | MoEngageSDKInApp |
| MOInApp.sharedInstance().showCampaign() | MoEngageSDKInApp.sharedInstance.showInApp() |
| MOInApp.sharedInstance().showNudge(at: NudgePositionTop) | MoEngageSDKInApp.sharedInstance.showNudge(atPosition: NudgePositionTop) |
| MOInAppNativeDelegate | MoEngageInAppNativeDelegate |
| func inAppShown(withCampaignInfoinappCampaign: MOInAppCampaign, foraccountMeta: MOAccountMeta) | func inAppShown(withCampaignInfoinappCampaign: MoEngageInAppCampaign, forAccountMetaaccountMeta: MoEngageAccountMeta) |
| `func inAppDismissed(withCampaignInfoinappCampaign: MOInAppCampaign, foraccountMeta: MOAccountMeta)` | `func inAppDismissed(withCampaignInfoinappCampaign: MoEngageInAppCampaign,forAccountMetaaccountMeta: MoEngageAccountMeta)` |
| `func inAppClicked(withCampaignInfoinappCampaign: MOInAppCampaign, andCustomActionInfocustomAction: MOInAppAction, foraccountMeta: MOAccountMeta)` | `func inAppClicked(withCampaignInfoinappCampaign: MoEngageInAppCampaign, andCustomActionInfocustomAction: MoEngageInAppAction, forAccountMetaaccountMeta: MoEngageAccountMeta)` |
| `func inAppClicked(withCampaignInfoinappCampaign: MOInAppCampaign, andNavigationActionInfonavigationAction: MOInAppAction, foraccountMeta: MOAccountMeta)` | `func inAppClicked(withCampaignInfoinappCampaign: MoEngageInAppCampaign, andNavigationActionInfonavigationAction: MoEngageInAppAction, forAccountMetaaccountMeta: MoEngageAccountMeta)` |
| `func selfHandledInAppTriggered(withInfoinappCampaign: MOInAppSelfHandledCampaign, foraccountMeta: MOAccountMeta)` | `func selfHandledInAppTriggered(withInfoinappCampaign: MoEngageInAppSelfHandledCampaign, forAccountMetaaccountMeta: MoEngageAccountMeta)` |
For more details, refer to the [InApp Native developer documentation](/docs/developer-guide/ios-sdk/in-app-messages/in-app-nativ).
# Cards
From SDK version `9.0.0`, we have updated methods related to Cards. Refer to the table below to use the updated one.
| SDK Version 8.x.x | SDK Version 9.0.0 |
| :--------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------------- |
| MOCards | MoEngageSDKCards |
| MOCardsDelegate | MoEngageCardsDelegate |
| MOCardsViewControllerDelegate | MoEngageCardsDelegate |
| `@objc optional func cardsSyncedSuccessfully(forAccountMeta accountMeta: MOAccountMeta)` | `@objc optional func cardsSyncedSuccessfully(forAccountMeta accountMeta: MoEngageAccountMeta)` |
# Push Notification
From SDK version `9.0.0`, we have updated methods related to Push Notifications. Refer to the table below to use the updated one.
| SDK Version 8.x.x | SDK Version 9.0.0 |
| :----------------------------------------------------------------------------------------------------------------------- | :-------------------------------------------------------------------------------------------------------------------------------- |
| MORichNotification | MoEngageSDKRichNotification |
| `MoEngage.sharedInstance().registerForRemoteNotification(withCategories: nil, withUserNotificationCenterDelegate: self)` | `MoEngageSDKMessaging.sharedInstance.registerForRemoteNotification(withCategories: nil, andUserNotificationCenterDelegate: self)` |
| `MoEngage.sharedInstance().setPushToken(deviceToken)` | `MoEngageSDKMessaging.sharedInstance.setPushToken(deviceToken)` |
| `MoEngage.sharedInstance().didFailToRegisterForPush()` | `MoEngageSDKMessaging.sharedInstance.didFailToRegisterForPush()` |
| `MoEngage.sharedInstance().userNotificationCenter(center, didReceive: response)` | `MoEngageSDKMessaging.sharedInstance.userNotificationCenter(center, didReceive: response)` |
| `MoEngage.sharedInstance().didReceieveNotificationinApplication(application, withInfo: userInfo)` | `MoEngageSDKMessaging.sharedInstance.didReceieveNotification(inApplication: application, withInfo: userInfo)` |
| `MoEngage.sharedInstance().setDisableBadgeReset(true)` | `MoEngageSDKMessaging.sharedInstance.disableBadgeReset(true)` |
| `MOMessaging.sharedInstance.setMessagingDelegate(self)` | `MoEngageSDKMessaging.sharedInstance.setMessagingDelegate(self)` |
# RealTimeTrigger:
From SDK version `9.0.0`, we have updated methods related to RealTime Trigger. Refer to the table below to use the updated one.
| SDK Version 8.x.x | SDK Version 9.0.0 |
| :---------------- | :------------------------- |
| MORealTimeTrigger | MoEngageSDKRealTimeTrigger |
For more details, refer to [Real-Time Triggers.](/docs/developer-guide/ios-sdk/push/optional/real-time-triggers)
# Location Triggered
From SDK version `9.0.0`, we have updated methods related to Location Trigger. Refer to the table below to use the updated one.
| SDK Version 8.x.x | SDK Version 9.0.0 |
| :--------------------------------------------------- | :------------------------------------------------------------- |
| MOGeofence | MoEngageSDKGeofence |
| `MOGeofenceHandler.sharedInstance().delegate = self` | `MoEngageSDKGeofence.sharedInstance.setGeofenceDelegate(self)` |
For more info on the Geofence feature, refer to our documentation [here.](/docs/developer-guide/ios-sdk/push/optional/location-triggered)
# Inbox
| SDK Version 8.x.x | SDK Version 9.0.0 |
| :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| MOInbox | MoEngageSDKInbox |
| MOInboxViewController | MoEngageInboxViewController |
| `func getInboxViewController(withUIConfiguration uiConfig: MOInboxUIConfiguration? = nil, withInboxWithControllerDelegate delegate: MOInboxViewControllerDelegate? = nil, forAppID appID: String? = nil, withCompletionBlock completionBlock: @escaping (MOInboxViewController?)->())` | `func getInboxViewController(withUIConfiguration uiConfig: MoEngageInboxUIConfiguration? = nil, withInboxWithControllerDelegate delegate: MoEngageInboxViewControllerDelegate? = nil, forAppID appID: String? = nil, withCompletionBlock completionBlock: @escaping (MoEngageInboxViewController?)->())` |
For more information on the Geofence feature, refer to this [document.](/docs/developer-guide/ios-sdk/push/basic/actionable-notifications)
# iOS 15
Source: https://moengage.com/docs/developer-guide/ios-sdk/os-updates/i-os-15
Review iOS 15 behavior changes that affect push notification appearance and MoEngage SDK features.
The following are the major behavior changes impacting the MoEngage Platform or MoEngage iOS SDK.
# Behavior Changes: Visual Updates to notifications
iOS 15 has brought in some new and exciting visual changes to the push notifications.
* Bigger App Icon
* Media can be placed below the text content compared to the previous version
* Smaller action buttons with support for icons
## What is MoEngage doing about it?
* We have released an SDK update (MORichNotification [5.2.0](/docs/release-notes/sdks/ios)) to have the current layout of Push template notifications similar to the native notification layout of iOS 15.
* Update the ‘preview’ capabilities during the campaign creation.
# Personalize SDK
Source: https://moengage.com/docs/developer-guide/ios-sdk/personalize/personalize-sdk
# Overview
The MoEngage iOS SDK provides a secure framework for delivering personalized campaigns. It simplifies integration by handling user identity and authentication internally, eliminating the need to manage API secrets or manual HTTPS calls. This allows you to fetch and track personalized campaign information directly through a streamlined, native interface.
Prerequisite
Before you can fetch personalized experiences, ensure you have called the SDK `initialize()` method within your application entry point ( `didFinishLaunchingWithOptions` on iOS). For more information, refer to [SDK initialization](https://www.moengage.com/docs/developer-guide/ios-sdk/sdk-integration/basic/sdk-initialization).
# Integration MoEngage Personalization
To install the `MoEngagePersonalization` through SPM, perform the following steps:
1. Navigate to **File** > **Add Package**.
2. Enter the appropriate repository URL:
* [https://github.com/moengage/apple-sdk.git](https://github.com/moengage/apple-sdk.git) (for MoEngage-iOS-SDK versions 9.23.0 and above)
* [https://github.com/moengage/MoEngage-iOS-SDK.git](https://github.com/moengage/MoEngage-iOS-SDK.git) (for other versions)
3. Select the master branch or your desired version.
4. Click **Add Package**.
5. Add the MoEngagePersonalization product to your app target.
Integrate the MoEngage Personalize framework by adding the dependency in the pod file as described.
**Information** CocoaPods is being deprecated. MoEngage recommends using Swift Package Manager for all new integrations. For more info, refer [here](/docs/developer-guide/ios-sdk/sdk-integration/basic/sdk-integration).
Add the following to your `Podfile`:
```ruby Cocoapods theme={null}
pod 'MoEngage-iOS-SDK/Personalization'
```
Then run:
```shellscript Shell wrap theme={null}
pod repo update
pod install
```
# Implementation Workflow
The `MoEngageSDKPersonalize` singleton is designed to simplify the retrieval and interaction with dynamic, personalized content on iOS. The SDK provides native Swift methods as well as Objective-C-compatible overloads.
## 1. Fetching Meta Experience
Before fetching any specific payload or experience, you must invoke the metadata call. This step provides the SDK with the necessary configuration and context to accurately process subsequent requests. The SDK offers dedicated methods for both Swift and Objective-C.
```swift Swift wrap theme={null}
public func fetchExperiencesMeta(
status: [MoEngageExperienceStatus],
onSuccess: @escaping MoEngageMetaSuccessCallback,
onFailure: @escaping MoEngagePersonalizeFailureCallback,
workspaceId: String? = nil
)
```
```objective-c Objective-c wrap theme={null}
@objc(fetchExperiencesMetaWithStatusRawValues:onSuccess:onFailure:workspaceId:)
public func fetchExperiencesMetaObjC(
statusRawValues: [NSNumber],
onSuccess: @escaping MoEngageMetaSuccessCallback,
onFailure: @escaping MoEngagePersonalizeFailureCallback,
workspaceId: String? = nil
)
```
The `onSuccess` callback returns an [`ExperienceCampaignsMetadata`](https://moengage.github.io/ios-api-reference/MoEngagePersonalization.html#/c:@M@MoEngagePersonalization@objc\(cs\)MoEngageExperienceCampaignMetaData) object containing all necessary metadata for campaign execution. Conversely, the `onFailure` callback provides a [`RequestFailureReasonCode`](https://moengage.github.io/ios-api-reference/MoEngagePersonalization.html#/c:@M@MoEngagePersonalization@E@MoEngageExperienceFailureReasonCode) and an optional message to identify the specific reason for the request's failure.
Always call the metadata fetch first to initialize core configurations based on the desired experience status. Use the standard method for Swift arrays, or the ObjC variant if passing raw NSNumber values from Objective-C code.
## 2. Fetch Personalized Content
Once metadata is fetched, you can retrieve the actual personalized payloads. You can fetch a single experience or multiple experiences simultaneously, with full support for contextual targeting.
The code snippet below to fetch a single experience is compatible with both Swift and Objective-C implementations:
```objective-c Objective-C wrap theme={null}
@objc public func fetchExperience(
experienceKey: String,
attributes: [String: String] = [:],
onSuccess: @escaping MoEngageExperienceSuccessCallback,
onFailure: @escaping MoEngagePersonalizeFailureCallback,
workspaceId: String? = nil
)
```
To fetch multiple experiences:
```swift Swift wrap theme={null}
public func fetchExperiences(
experienceKeys: Set,// EXPERIENCE_KEY must be one of the keys returned in the fetchExperiencesMeta call.
// Using a key that is not part of the meta call or incorrect key will result in an invalid key error.
attributes: [String: String] = [:],
onSuccess: @escaping MoEngageExperienceSuccessCallback,
onFailure: @escaping MoEngagePersonalizeFailureCallback,
workspaceId: String? = nil
)
```
```objective-c Objective-c wrap theme={null}
// MARK: - Multiple Experiences Fetch @objc(fetchExperiencesWithKeys:attributes:onSuccess:onFailure:workspaceId:)
public func fetchExperiencesObjC(
experienceKeys: [String], // EXPERIENCE_KEY must be one of the keys returned in the fetchExperiencesMeta call.
// Using a key that is not part of the meta call or incorrect key will result in an invalid key error.
attributes: [String: String] = [:],
onSuccess: @escaping MoEngageExperienceSuccessCallback,
onFailure: @escaping MoEngagePersonalizeFailureCallback,
workspaceId: String? = nil
)
```
The `onSuccess` callback returns an [`ExperienceCampaignsResult`](https://moengage.github.io/ios-api-reference/MoEngagePersonalization.html#/c:@M@MoEngagePersonalization@objc\(cs\)MoEngageExperienceCampaignsResult) object containing all necessary metadata for campaign execution. Conversely, the `onFailure` callback provides a [`RequestFailureReasonCode`](https://moengage.github.io/ios-api-reference/MoEngagePersonalization.html#/c:@M@MoEngagePersonalization@E@MoEngageExperienceFailureReasonCode) and an optional message to identify the specific reason for the request's failure.
You can fetch :
* Single Experiences: Retrieve a specific payload using a single experienceKey.
* Bulk Experiences: Retrieve multiple payloads at once. In Swift, pass a Set\. For Objective-C, use the `fetchExperiencesObjC` method which accepts an NSArray bridged as \[String].
Use cases:
**Contextual Targeting**: Pass a dictionary of attributes (e.g., \["current\_page": "home", "cart\_value": "500"]) during the fetch. This enables real-time, state-dependent content delivery (e.g., showing a "Free Shipping" banner if the cart value meets a threshold).
## 3. Track Impressions
To accurately measure campaign performance, you must track user interactions after rendering the personalized content on the UI. The below-mentioned tracking code snippets are compatible with both Swift and Objective-C implementations.
### 3a. Track Impressions for Experience Campaigns
```objective-c Objective-C wrap theme={null}
// MARK: - Tracking
/// Tracks impression for an experience.
@objc public func experienceShown(
campaign: MoEngageExperienceCampaign, //campaign is a placeholder for the campaign objects returned by the fetchExperiences function. They contain the metadata and payload required by the SDK to attribute impressions and clicks to the correct campaign.
workspaceId: String? = nil
)
/// Tracks impressions for multiple experiences.
@objc public func experiencesShown(
campaigns: [MoEngageExperienceCampaign],
workspaceId: String? = nil
)
```
You can use these methods to log "Impressions" (`experienceShown / experiencesShown`) when the UI renders the content.
### 3b. Track Impressions for Offering Campaigns
[Offerings](/docs/user-guide/decisioning/offer-decisioning/create-offerings) are a distinct subtype of personalized content. The SDK provides dedicated tracking functions that accept offering-specific attributes. The below-mentioned tracking code snippets are compatible with both Swift and Objective-C implementations.
```objective-c Objective-C wrap theme={null}
// MARK: - Offering Tracking
/// Tracks impression for an offering within an experience.
@objc public func offeringShown(
offeringPayload: [String: Any],
workspaceId: String? = nil
)
/// Tracks impressions for multiple offerings.
@objc public func offeringsShown(
offeringPayloads: [[String: Any]],
workspaceId: String? = nil
)
```
These methods send the full offering dict (one element from the `offerings` array in `campaign.payload`) to the server, providing highly granular analytics for custom offers.
## 4. Track Clicks
### 4a. Track Clicks for Experience Campaigns
```objective-c Objective-C wrap theme={null}
/// Tracks click for an experience.
@objc public func experienceClicked(
campaign: MoEngageExperienceCampaign,
workspaceId: String? = nil
)
```
You can use these methods to log "Clicks" (`experienceClicked`) when the user interacts with the UI element.
### 4b. Track Clicks for Offering Campaigns:
[Offerings](/docs/user-guide/decisioning/offer-decisioning/create-offerings) are a distinct subtype of personalized content. The SDK provides dedicated tracking functions that accept offering-specific attributes. The below-mentioned tracking code snippets are compatible with both Swift and Objective-C implementations.
```objective-c Objective-C wrap theme={null}
/// Tracks click for an offering.
@objc public func offeringClicked(
campaign: MoEngageExperienceCampaign,
offeringPayload: [String: Any],
workspaceId: String? = nil
)
```
You can use these Offering-specific functions only if the data is part of an offering payload. For all other experience data, use the standard experience shown/clicked functions.
These methods send the full offering dict (one element from the `offerings` array in `campaign.payload`) to the server, providing highly granular analytics for custom offers.
For more information, refer to the [API documentation](https://moengage.github.io/ios-api-reference/MoEngagePersonalization.html#/c:@M@MoEngagePersonalization@objc\(cs\)MoEngageSDKPersonalize).
# FAQs
No. The SDK returns raw JSON payloads. You are responsible for parsing the data and building the UI components (e.g., banners or carousels).
No. The SDK does not download or cache media assets. Use a standard media loading library to handle images, videos, or fonts referenced in the JSON.
Yes. You can fetch up to 25 experiences in a single call. If you exceed this, the SDK returns the 25 most recently updated experiences and notifies you of the unfulfilled keys.
The SDK returns an empty payload along with a standardized error code (e.g., NETWORK\_ERROR). For more information on all the errors, refer [here](https://moengage.github.io/ios-api-reference/MoEngagePersonalization.html#/c:@M@MoEngagePersonalization@objc\(cs\)MoEngageExperienceFailureReason).
# Custom Notification Handling
Source: https://moengage.com/docs/developer-guide/ios-sdk/push/advanced/custom-notification-handling
Customize badge reset, notification sounds, and action handling in iOS push with the MoEngage SDK.
# Disable Badge Reset
By default, the SDK sets the notification badge count to **0** on every app launch and this also clears the notifications in the device notification center. In case if you would like to keep the notifications even after the App Launch then disable badge reset by calling the below method before MoEngage SDK is initialized.
```swift Swift wrap theme={null}
MoEngageSDKMessaging.sharedInstance.disableBadgeReset(true)
```
```objective-c Objective C wrap theme={null}
[[MoEngageSDKMessaging sharedInstance] disableBadgeReset:true];
```
# Custom Sound for Notification
You can have a custom tone for notifications of your app. iOS platform supports .aiff , .caf and .wav files for custom Notification tone. For this make sure the sound file of tone is included in your app bundle. Once this is done make sure to provide the sound filename for Notification Sound(In Rich Content Section) while creating the campaign in the dashboard as shown below, and it should work:
# Notification Actions
MoEngage provides several actions that can be included in push notifications to enhance user engagement and interaction. Here are some of the actions provided by MoEngage push notifications:
1. RichLanding: The provided URL will be opened within the app using the Safari View Controller, which ensures that the user remains within the application while accessing the URL. This functionality is handled by the SDK.
2. Navigate To Screen: Implement the callback method mentioned in the **Notification Click Callback in the App** of the [doc](/docs/developer-guide/ios-sdk/push/basic/ios-push-integration-tutorial#step-1-implement-notification-service-extension-nse) to perform the required action.
3. Deeplink: It is used to navigate users directly to a specific location or content within a mobile app.
If your application is below iOS 13, then a deeplink callback is received in the below ***AppDelegate*** method:
```swift Swift wrap theme={null}
import UIKit
// Custom Scheme Link
func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool {
//Call only if MoEngageAppDelegateProxyEnabled is NO in Info.plist
MoEngageSDKAnalytics.sharedInstance.processURL(url)
}
// Universal Links
func application(_ application: UIApplication, continue userActivity: NSUserActivity, restorationHandler: @escaping ([UIUserActivityRestoring]?) -> Void) -> Bool {
if userActivity.activityType == NSUserActivityTypeBrowsingWeb ,
let incomingURL = userActivity.webpageURL{
//Call only if MoEngageAppDelegateProxyEnabled is NO in Info.plist
MoEngageSDKAnalytics.sharedInstance.processURL(incomingURL)
}
//rest of the implementation
return true
}
```
```objective-c Objective C wrap theme={null}
// Custom Scheme Link
- (BOOL)application:(UIApplication *)app openURL:(NSURL *)url options:(NSDictionary *)options {
return true;
}
// Universal Link
- (BOOL)application:(UIApplication *)application continueUserActivity:(NSUserActivity *)userActivity restorationHandler:(void (^)(NSArray * _Nullable))restorationHandler {
return true;
}
@end
```
If your application is above iOS 13, then a deeplink callback is received in the below ***SceneDelegate*** method:
```swift Swift wrap theme={null}
import UIKit
// Custom Scheme Link
func scene(_ scene: UIScene, openURLContexts URLContexts: Set) {
let url = URLContexts.first?.url
MoEngageSDKAnalytics.sharedInstance.processURL(url)
}
// Universal Scheme Link
func scene(_ scene: UIScene, continue userActivity: NSUserActivity) {
if let url = userActivity.webpageURL {
MoEngageSDKAnalytics.sharedInstance.processURL(url)
}
}
}
```
**Note**
* While implementing deep links, make sure that you have added the apps URL Scheme to **LSApplicationQueriesSchemes** array in Info.plist to whitelist your app. Without this, the deep links won't work post iOS9.
# Notification Payload
An example of the push payload sent to the app:
```json iOS Push Payload wrap theme={null}
{
"aps": {
"alert": {
"title": "Notification Title",
"subtitle": "Notification Subtitle",
"body": "Notification Body"
},
"badge": 1,
"sound": "default",
"category": "INVITE_CATEGORY",
"content-available": 1,
"mutable-content": 1
},
"app_extra": {
"moe_deeplink": "moeapp://screen/settings",
"screenName": "Screen Name",
"screenData": {
"key1": "val1",
"key2": "val2"
}
},
"moengage": {
"silentPush": 1,
"cid": "55f2ba15a4ab4104a287bf88",
"app_id": "DAO6UGZ73D9RTK8B5W96TPYN_DEBUG",
"moe_campaign_id": "55f2ba15a4ab4104a287bf88",
"moe_campaign_name": "Campaign Name",
"inbox_expiry": "1571905058",
"webUrl": "https://google.com",
"couponCode": "APP200",
"media-attachment": "https://image.moengage.com/testImg.png",
"media-type": "image"
}
}
```
Description of different keys in the payload:
* **aps**: This key is used by the iOS to display the notification, and the following are the keys present within it:
* **alert** : Message Content.
* **title** : Gives Notification title.
* **subtitle** : Gives Notification subtitle.
* **body** : Gives the message body of the notification
* **badge**: Gives the badge number to be displayed on top of the App Icon. MoEngage platform supports only two possible values i.e, 0/1. If the value is 1 then the SDK will increment the badge number on the app icon and if it's 0 then the badge number will be reset and there will be no badge displayed on the app icon.
* **sound**: This key gives the filename of the audio file to be played on receiving the notification. If no filename is provided while creating the campaign, to play the os default sound this key is set to the value "default".
* **category**: This key is used by OS for deciding the set of action buttons to be displayed for the notification. Also, the same category is used by OS to decide which Notification Content Extension target to display if present.
* **content-available**: If the value of this key is set to 1, then if the app is present in the background it will get a callback(`application:didReceiveRemoteNotification:fetchCompletionHandle`) to refresh the app content in background. Use this key only if you have to process the push notification in background. By default, this key will be unset.
* **mutable-content**: This key is by default set to 1 for all the campaigns, this is to make sure that the Notification Service Extension target gets the callback on receiving the notification to be processed by MORichNotification. If set to 0 the extension target won't get the callback.
* **app\_extra**: This key will contain the keys which are to be used by App Developers, i.e, Custom key value pairs and screenName for navigation.
* **moe\_deeplink**: This key contains the deeplinking URL if provided during the campaign creation. The SDK will process this key and will attempt to open the deeplink URL if it's valid.
* **screenName**: This key gives screen name where the user has to be navigated on clicking the notification. This navigation is not done by the SDK. The possible values for this parameter are something which app developers will have to define in their project. If provided while creating the campaign, it will be present in the notification payload. And implementing the part to parse and get `screenName` parameter's value and to navigate to the mentioned screen has to be implemented by the app developers.
* **screenData**: This contains the custom key-value pairs entered while creating the campaign, which can be made use by the app developers for any of their use-cases.
* **moengage** : This will contain keys which are to be used by SDK, app developers should not be making any change to this part of the payload and also avoid using this part of the payload, as we may update the structure of this part of payload as per our need. (with the exception being cid, media-attachment, media-type, app\_id which we will not change)
* **silentPush**: This key is present and set to `1` for silent pushes sent from MoEngage.
* **cid**: Unique ID for the campaign.
* **app\_id**: The App ID of the account where the campaign was created.
* **moe\_campain\_id** and **moe\_campaign\_name** : Used by analytics module to track attributes for Notification related events.
* **inbox\_expiry**: This key gives the timestamp at which the notification will be deleted from the app inbox.
* **webUrl**: This key contains the Rich-landing URL if provided during the campaign creation. The SDK will process this key and will open the URL(if valid) in an instance of SFSafariViewController. Use Rich-landing action if you wish to open a web page inside the app on click of the push notification. For e.g. `webUrl` - [https://www.google.com](https://www.google.com/).
* **couponCode**: This key contains the coupon code if provided during the campaign creation. On clicking the notification, if this key is present in the push payload the SDK will display an alert with the coupon code and will give an option to user to copy the coupon to the os clipboard.For e.g. `couponCode` - APP200.
* **media-attachment:** The media-attachment key in the payload gives you the URL of the media which you can download.
* **media-type**: Type of media present in the URL given in media-attachment i.e, image/audio/video.
**HTTP URLs**
Http URL's aren't supported unless explicitly specified in the plist. You will have to include ***App Transport Security Setting*** **s** Dictionary in your Info.plist and inside this set ***Allow Arbitrary Loads*** to ***YES***.
# Actionable Notifications
Source: https://moengage.com/docs/developer-guide/ios-sdk/push/basic/actionable-notifications
Add custom action buttons to standard iOS push notifications using the MoEngage SDK.
Actionable notifications let you add custom action buttons to the standard iOS push notifications. It also gives the user a quick and easy way to perform relevant tasks in response to a notification.
# How to implement Actionable Notifications?
## Define a category
To use actionable notifications with MoEngage SDK, you have to define the actions, group them into categories, as shown in the example, and pass them as a parameter while registering for push notifications.
```swift Swift wrap theme={null}
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterDelegate{
var window: UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
//--- Rest of Implementation
//For registering for remote notification
let categories = self.getCategories()
MoEngageSDKMessaging.sharedInstance.registerForRemoteNotification(withCategories: categories, andUserNotificationCenterDelegate:self)
//--- Rest of Implementation
return true
}
//Example to define categories
//This method gives categories
func getCategories() -> Set{
let acceptAction = UNNotificationAction.init(identifier: "ACCEPT_IDENTIFIER", title: "Accept", options: .authenticationRequired)
let declineAction = UNNotificationAction.init(identifier: "DECLINE_IDENTIFIER", title: "Decline", options: .destructive)
let maybeAction = UNNotificationAction.init(identifier: "MAYBE_IDENTIFIER", title: "May Be", options: .foreground)
let inviteCategory = UNNotificationCategory.init(identifier: "INVITE_CATEGORY", actions: [acceptAction,declineAction,maybeAction], intentIdentifiers: [], options: .customDismissAction)
let categoriesSet = Set.init([inviteCategory])
return categoriesSet;
}
}
```
```objective-c Objective C wrap theme={null}
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOption
{
---------
NSSet* categories = [self getNotificationCategories];
[[MoEngageSDKMessaging sharedInstance] registerForRemoteNotificationWithCategories:categories andUserNotificationCenterDelegate:self];
}
---------
return YES;
}
//Example to define categories
//This method gives categories
-(NSSet*)getNotificationCategories{
UNNotificationAction *acceptAction = [UNNotificationAction actionWithIdentifier:@"ACCEPT_IDENTIFIER" title:@"Accept" options:UNNotificationActionOptionAuthenticationRequired];
UNNotificationAction *declineAction = [UNNotificationAction actionWithIdentifier:@"DECLINE_IDENTIFIER" title:@"Decline" options:(UNNotificationActionOptionDestructive)];
UNNotificationAction *maybeAction = [UNNotificationAction actionWithIdentifier:@"MAYBE_IDENTIFIER" title:@"May Be" options:UNNotificationActionOptionNone];
UNNotificationCategory* inviteCategory = [UNNotificationCategory categoryWithIdentifier:@"INVITE_CATEGORY"actions:@[acceptAction,maybeAction, declineAction,opt4Action] intentIdentifiers:@[] options:UNNotificationCategoryOptionNone];
NSSet *categories = [NSSet setWithObjects:inviteCategory,nil];
return categories;
}
```
**Notification Categories**
MoEngage recommended not to change the actions grouped in a category across the app versions, as it will lead to users seeing different actions for the same category across different app versions.
# APNS Authentication Key
Source: https://moengage.com/docs/developer-guide/ios-sdk/push/basic/apns-authentication-key
Generate and upload an APNs Authentication Key to the MoEngage Dashboard for iOS push delivery.
# APNS Auth Keys
APNS Auth keys are the recommended method to enable sending push notifications to your app installed on iOS devices.
To send push notifications to iOS users, it is required to generate the APNs Auth Key file for your application and upload it to the MoEngage dashboard.
# Steps to create APNS Auth Key
To create an APNS auth key, you will need to do the following:
Visit the [Apple Developer Member Center](https://idmsa.apple.com/IDMSWebAuth/signin?appIdKey=891bd3417a7776362562d2197f89480a8547b108fd934911bcbea0110d07f757\&path=%2Faccount%2F\&rv=1) and sign in with your credentials.
Select the Certificates, Identifiers & Profiles on the left pane.
On the certificates page, click on 'Keys' on the left pane as shown below.
In the Keys page that opens, click on the '+' icon to create a new auth key.
In the 'Register a New Key' page that opens, enter the key name and choose the Apple Push Notifications Service (APNS) in the list available below.
Click on the Register Button.
The Download your Keys page opens.
Click on Download to download your Auth key file. Please note that you can download the auth key file only once.
Copy the Key ID (highlighted in the image above) available on the download page. This is required to configure push notifications in the MoEngage dashboard.
Once added, the Auth Key is listed under Keys as shown below.
# Configuring the Auth key in the Dashboard
The following details are necessary to configure the APNS authentication key in the MoEngage Dashboard.
1. Navigate to **Settings > Channels > Push > App Push**.
2. Select iOS (APNS) in the Platforms available on the menu at the top.
## APNS Authentication Key File
The APNS Authentication Key File downloaded using the steps mentioned above needs to be uploaded to the dashboard.
## Team ID
To get the Team ID details, click on the account name in the top right corner, then select **View Account**. The following screen is displayed. Copy the Team ID from here.
## Key ID
As explained in step 8 above, the Key ID is available when you generate the APNS auth key.
## Bundle ID
The App Bundle Identifier is case-sensitive and available in Xcode. Refer to the screenshot below to get the Bundle ID in Xcode.
## Bundle ID for iPad
If you have different apps for iPhone and iPad, upload a different bundle ID for iPad. It is available in the Xcode for the iPad app.
# APNS Certificate/PEM file (legacy)
Source: https://moengage.com/docs/developer-guide/ios-sdk/push/basic/apns-certificate-pem-file-legacy
Create and upload a legacy APNs certificate in PEM format for iOS push notifications with MoEngage.
**Note**
Effective January 15, 2026, MoEngage will deprecate the legacy APNs Provider Certificate (.pem) method. After this date, existing certificates cannot be renewed, and all new uploads must use the [APNs Authentication Key (.p8)](/docs/developer-guide/ios-sdk/push/basic/apns-authentication-key).
MoEngage strongly recommends migrating to the .p8 Auth Key as soon as possible to ensure uninterrupted service. This upgrade offers the following benefits:
* **Permanent validity**: Auth Keys never expire, eliminating the need for annual certificate renewals.
* **Faster delivery**: Stateless token-based authentication provides faster communication than certificate-based methods.
* **Modern feature support:** The new standard supports exclusive iOS capabilities, such as Live Activities.
In order to send push notifications to your app, an APNS certificate is needed for your app, and the same has to be converted to a `.pem` format and uploaded to our dashboard. Follow the steps below to do the same:
# Creating APNS Certificate
## Generating the Certificate Signing Request (CSR)
First Open **Keychain Access** on your Mac and choose the menu option **Request a Certificate from a Certificate Authority** as shown below:
You should now see the window shown below. Enter your email address here and your app name for Common Name. Check `Saved to disk` and click Continue. Save the file as “.certSigningRequest”:
## Create an APNS Certificate in Developer Account
Log-in to your developer account and go to **Certificates, Identifiers and Profiles**. Select **Certificates** Section and click on the *+* icon to create a new certificate. In **Create a New Certificate** screen select `Apple Push Notification service SSL(Sandbox & Production)` and click Continue as shown below:
Next, select the App ID(App Bundle ID) for which you are creating the APNS certificate and click Continue
**APP ID Selection Note**
Ensure **Push Notification** capability is enabled for the App(App ID selected) you are creating an APNS certificate for.
Now choose the `certificate signing request` file created in the first step and click Continue as shown below:
That's it! Your APNS certificate has been successfully created. Go ahead and download the same and open it to include it in your Mac `Keychain Access`
# Converting Certificate to PEM format
Follow the below steps to convert the APNS certificate obtained in the previous step to `.pem` file:
1. First, go to **Keychain Access** and select your APNS certificate, then right-click on it select **Export** option. Now export your certificate in `.p12` format. You will be prompted to provide a password for `.p12`, do the same.
2. Convert the `.p12` file obtained in the previous step into a `.pem` file by using **openssl** commands as shown below, here you will have to provide the `Import Password`:
```ruby Ruby wrap theme={null}
openssl pkcs12 -in p12Cert.p12 -out pemAPNSCert.pem -nodes -legacy
Enter Import Password:
MAC verified OK
```
**Note**
You can also go the SSL Converter [here](https://www.sslshopper.com/ssl-converter.html) and convert your `.p12` file to `.pem`.
# Verify .pem file
Before uploading the certificate to MoEngage Dashboard, verify the `.pem` file obtained:
1. First, open the `.pem` in a text editor to view its content. The certificate content should be in format as shown below. Make sure the pem file contains both Certificate content(from `BEGIN CERTIFICATE` to `END CERTIFICATE`) as well as Certificate Private Key (from `BEGIN PRIVATE KEY` to `END PRIVATE KEY`)
```text Text theme={null}
Bag Attributes
friendlyName: Apple Push Services:
localKeyID: <>
subject=<>
-----BEGIN CERTIFICATE-----
-----END CERTIFICATE-----
Bag Attributes
friendlyName: <>
localKeyID: <>
Key Attributes:
-----BEGIN PRIVATE KEY-----
-----END PRIVATE KEY-----
```
2. Also, you check the validity of the certificate by going to SSLShopper [Certificate Decoder](https://www.sslshopper.com/certificate-decoder.html) and pasting the Certificate Content (from `BEGIN CERTIFICATE` to `END CERTIFICATE`) to get all the info about the certificate as shown below:
As you can see, the Common Name should contain **Apple Push Services** and the App's Bundle ID. Confirm the organization's information and also the **Validity** of the Certificate. Once everything is verified, upload the certificate to our dashboard.
# Uploading PEM file to MoEngage Dashboard
1. Navigate to **Settings > Channels > Push > App Push**.
2. Select iOS (APNS) in the Platforms available on the menu at the top.
3. Click on **APNS provider certificate**.
4. Upload the *.pem* file. Enter the password for the *.pem* file, or leave it blank if there isn't any.
**iPad Support**
In case you have different apps for iPhones and iPads(Different Bundle IDs), set up certificates for both iOS and iPad separately in the dashboard in order to be able to send the messages to all the devices.
# Test/Live Builds
* If you are testing the app on **Test Flight or a live app store build**, make sure you upload the ad-hoc or production pem to our dashboard. And also in this case you have to send push notifications from the **Live environment** of your account.
* For the dev build, you can upload the development or production certificate in the dashboard, but make sure that you create your campaign in the **Test environment**, as you cannot send push notifications to the dev build from the Live environment.
# iOS Push Integration Tutorial
Source: https://moengage.com/docs/developer-guide/ios-sdk/push/basic/ios-push-integration-tutorial
Step-by-step instructions for integrating the MoEngage iOS SDK to send, receive, and track rich push notifications.
This article provides step-by-step instructions for integrating the MoEngage iOS SDK, enabling your application to send, receive, and track rich push notifications. By following these instructions, you will implement a comprehensive setup that supports rich media content, including images and GIFs, and ensures reliable analytics.
This article covers the following essential topics:
* **Setting up the Notification Service Extension (NSE)**: Enable rich media content (images, GIFs) and reliable impression tracking across all app states.
* **Configuring the AppDelegate**: Implement push notification registration, device token handling, and user interaction callbacks.
* **Validating the Integration**: Follow a process to test notification delivery, rich media display, and analytics tracking.
* **Implementing Advanced Features**: Enhance notifications with custom push templates, actionable buttons, and badge count control.
**Prerequisites**
To enable push notifications:
1. **Integrate the MoEngage iOS SDK**: Use CocoaPods, Swift Package Manager (SPM), or manual framework import as detailed in the [MoEngage documentation](https://www.moengage.com/docs/developer-guide/ios-sdk/sdk-integration/basic).
2. **Configure APNs credentials in the MoEngage UI**:
* MoEngage requires either an APNs Authentication Key (.p8) or an [APNs Certificate](https://www.moengage.com/docs/developer-guide/ios-sdk/push/basic/apns-certificate-pem-file-legacy) (.pem) to communicate with Apple Push Notification Service (APNs).
* **Recommendation**: Use an APNs Authentication Key because it is non-expiring. Refer to the [APNs Authentication Key Setup Guide](https://www.moengage.com/docs/developer-guide/ios-sdk/push/basic/apns-authentication-key).
3. Ensure you have added **Push Notifications** capability in the main target by navigating to **Target** -> **Signing & Capabilities**.
## Step 1: Implement Notification Service Extension (NSE)
NSE enables rich push notifications (including images, video, and GIFs) and tracks notification impressions across all app states.
After you configure the NSE, the SDK tracks impressions for **both text-only and rich push notifications** in all app states — foreground, background, and killed. Impression tracking does not require a rich media payload.
* Verify that the Notification Service Extension is configured and the MoEngageRichNotification framework is integrated in your iOS project. If a device receives a rich push notification payload with a service extension configured and the MoEngageRichNotification framework is not added, the SDK throws a fatal error in `DEBUG` builds. In release builds, the rich content fails to render and the notification falls back to the standard payload.
* If you are upgrading an existing app and these strict `DEBUG` validations cause disruptive crashes while you refactor your tracking code, you can temporarily opt out by calling `disableIntegrationValidator()` during SDK initialization. Use this strictly as a stopgap for phased upgrades, and aim to remove the opt-out once your attribute call sites are properly validated.
1. In Xcode, navigate to **File** > **New** > **Target**… > **Notification Service Extension**.
2. Enter a name for your NSE target.
To install the `MoEngageRichNotification` through SPM, perform the following steps:
1. Navigate to **File** > **Add Package**.
2. Enter the appropriate repository URL:
* [https://github.com/moengage/apple-sdk.git](https://github.com/moengage/apple-sdk.git) (for MoEngage-iOS-SDK versions 9.23.0 and above)
* [https://github.com/moengage/MoEngage-iOS-SDK.git](https://github.com/moengage/MoEngage-iOS-SDK.git) (for other versions)
3. Select the master branch or your desired version.
4. Click **Add Package**.
5. Target the installed package to your NSE file.
**Information** CocoaPods is being deprecated. MoEngage recommends using Swift Package Manager for all new integrations. For more info, refer \[here]\(([https://www.moengage.com/docs/developer-guide/ios-sdk/sdk-integration/basic/sdk-integration](https://www.moengage.com/docs/developer-guide/ios-sdk/sdk-integration/basic/sdk-integration)).
Add the following to your `Podfile`:
```ruby Ruby wrap theme={null}
target 'YourApp' do
use_frameworks!
pod 'MoEngage-iOS-SDK'
end
target 'MoEngageNotificationService' do
use_frameworks!
pod 'MoEngage-iOS-SDK/RichNotification'
end
```
Then run:
```shellscript Shell wrap theme={null}
pod repo update
pod install
```
To set up an App Group for seamless communication between your main app and NSE, perform the following steps:
1. **Enable App Groups**:
* For both your main application target and your NSE target, navigate to **Signing and Capabilities**.
* Click **+ Capability** and select **App Groups**.
2. **Create App Group ID**:
* Create a new App Group ID (for example, `group.com.yourcompany.appname`).
* **Crucially**, ensure this exact App Group ID is enabled for both the main app and the NSE targets.
3. **Update AppDelegate**: Modify your `AppDelegate.swift` in your iOS application to set the App Group ID as shown below:
```swift Swift wrap theme={null}
// In your main AppDelegate.swift - didFinishLaunchingWithOptions
let sdkConfig = MoEngageSDKConfig(appId: "YOUR_WORKSPACE_ID", dataCenter: .data_center_01)
sdkConfig.appGroupID = "group.com.yourcompany.appname" // Your App Group ID
```
```objective-c Objective C wrap theme={null}
MoEngageSDKConfig *sdkConfig = [[MoEngageSDKConfig alloc] initWithAppId:@"YOUR_WORKSPACE_ID" dataCenter:MoEngageDataCenterData_center_01];
sdkConfig.appGroupID = @"group.com.yourcompany.appname"; // Add your App Group ID here
[[MoEngage sharedInstance] initializeDefaultTestInstance:sdkConfig];
```
Replace the entire content of the generated `NotificationService.swift` file with the following:
```swift Swift wrap theme={null}
import UserNotifications
import MoEngageRichNotification
class NotificationService: UNNotificationServiceExtension {
var contentHandler: ((UNNotificationContent) -> Void)?
var bestAttemptContent: UNMutableNotificationContent?
override func didReceive(_ request: UNNotificationRequest, withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void) {
self.contentHandler = contentHandler
bestAttemptContent = (request.content.mutableCopy() as? UNMutableNotificationContent)
//Tell the MoEngage SDK about the App Group ID
MoEngageSDKRichNotification.setAppGroupID("group.com.yourcompany.appname")
// Step 2: Pass the notification to the MoEngage SDK
// The SDK will download rich media and track the impression before calling your completion handler
MoEngageSDKRichNotification.handle(richNotificationRequest: request, withContentHandler: contentHandler)
}
}
```
```objective-c Objective C wrap theme={null}
#import
@import MoEngageRichNotification;
@interface NotificationService ()
@property (nonatomic, strong) void (^contentHandler)(UNNotificationContent *contentToDeliver);
@property (nonatomic, strong) UNMutableNotificationContent *bestAttemptContent;
@end
@implementation NotificationService
- (void)didReceiveNotificationRequest:(UNNotificationRequest *)request withContentHandler:(void (^)(UNNotificationContent * _Nonnull))contentHandler {
@try {
self.contentHandler = contentHandler;
self.bestAttemptContent = [request.content mutableCopy];
[MoEngageSDKRichNotification setAppGroupID:@"group.com.yourcompany.appname"];
[MoEngageSDKRichNotification handleWithRichNotificationRequest: request withContentHandler:contentHandler];
} @catch (NSException *exception) {
NSLog(@"MoEngage : exception : %@",exception);
}
}
@end
```
### Media Requirements
The NSE downloads the image, audio, or video before the notification is displayed. The following requirements apply to all rich push and push template media.
#### Serve Media Over HTTPS
Serve push media over HTTPS. [App Transport Security](https://developer.apple.com/documentation/bundleresources/information-property-list/nsapptransportsecurity) blocks HTTP downloads, and the notification is displayed without the media.
To serve media over HTTP, set [`NSAllowsArbitraryLoads`](https://developer.apple.com/documentation/bundleresources/information-property-list/nsapptransportsecurity/nsallowsarbitraryloads) to `true` in the `Info.plist` of your **Notification Service Extension**:
```xml Info.plist theme={null}
NSAppTransportSecurityNSAllowsArbitraryLoads
```
Add the `NSAppTransportSecurity` dictionary to the NSE's `Info.plist`, not to the app target's `Info.plist`. In Xcode, the NSE's `Info.plist` is in the extension folder, next to `NotificationService.swift`. The app target's `Info.plist` applies only to requests made by your app, so the extension requires its own entry.
#### Keep Media Within Apple's Attachment Size Limits
Apple limits notification attachments to 5 MB for audio, 10 MB for images, and 50 MB for video. These are Apple's limits, not MoEngage's. If a file exceeds the limit for its media type, iOS discards the file and displays the notification without the media. Refer to Apple's [UNNotificationAttachment](https://developer.apple.com/documentation/usernotifications/unnotificationattachment) documentation for the current limits and supported file formats.
#### Test Each Media Type Separately
A working image or audio file does not confirm that video works. When a download fails, the NSE does not report an error — the notification is still delivered and displays only the title and body. If video does not display but image and audio do, check that the video URL uses HTTPS and that the file is within Apple's size limit for video.
Check the image format as well. iOS displays only [the formats listed by Apple for notification attachments](https://developer.apple.com/documentation/usernotifications/unnotificationattachment#Supported-File-Types). WebP (`.webp`) images are not among them and are not displayed. Use JPG, PNG, or GIF instead.
## Step 2: Implement Push Handling in AppDelegate
Integrate push notification registration and token management within your `AppDelegate` to ensure proper communication with Apple Push Notification service (APNs) and MoEngage. This setup allows MoEngage to effectively map APNs tokens with your users.
Starting with iOS SDK [11.0.0](/docs/release-notes/sdks/ios#22nd-july-2026), `setPushToken(_:)` returns a typed task object instead of `Void`, so you can observe per-call success or failure through `.onSuccess` / `.onFailure` or the async `result()` method. Direct calls like the one above compile unchanged.
### Choose a Notification Registration Method
To register for remote notifications at launch, call exactly one of the following methods based on your desired user experience:
* **Standard Notifications (Direct Opt-In)**: Presents a system prompt for permission to deliver full notifications with banners and sounds upon user approval. Calling `registerForRemoteNotification()` displays a permission pop-up to the user if they are not already opted in.
* **Provisional Notifications (Quiet Opt-In)**: Delivers notifications silently to the Notification Center without an initial prompt, allowing the user to opt into full delivery later.
For more information, refer to [iOS Push Permission and Reachability](https://www.moengage.com/docs/user-guide/campaigns-and-channels/mobile-push/notification-features-and-behavior/ios-push-permission-and-reachability).
```swift Swift wrap theme={null}
import UIKit
import MoEngageSDK
import UserNotifications
@main
class AppDelegate: UIResponder, UIApplicationDelegate {
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// method to use provisional push
MoEngageSDKMessaging.sharedInstance.registerForRemoteProvisionalNotification()
return true
}
}
// To display standard notifications later in the user's journey, call the following method after the app starts.
MoEngageSDKMessaging.sharedInstance.registerForRemoteNotification()
```
```objective-c Objective C wrap theme={null}
#import "AppDelegate.h"
#import
#import MoEngageSDK/MoEngageSDKMessaging.h
@implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// method to use provisional push
[[MoEngageSDKMessaging sharedInstance] registerForRemoteProvisionalNotificationWithCategories:nil andUserNotificationCenterDelegate:nil];
return YES;
}
@end
// To display standard notifications later in the user's journey, call the following method after the app starts.
[[MoEngageSDKMessaging sharedInstance] registerForRemoteNotificationWithCategories:nil andUserNotificationCenterDelegate:nil];
```
To register for standard push notifications, use the following MoEngage SDK code:
```swift Swift wrap theme={null}
import UIKit
import MoEngageSDK
import UserNotifications
@main
class AppDelegate: UIResponder, UIApplicationDelegate {
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
MoEngageSDKMessaging.sharedInstance.registerForRemoteNotification()
return true
}
}
```
```objective-c Objective C wrap theme={null}
#import "AppDelegate.h"
#import
#import MoEngageSDK/MoEngageSDKMessaging.h
@implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Override point for customization after application launch.
[[MoEngageSDKMessaging sharedInstance] registerForRemoteNotificationWithCategories:nil andUserNotificationCenterDelegate:nil];
return YES;
}
@end
```
### App Delegate Method Swizzling
App Delegate Method Swizzling, a runtime technique utilized by the MoEngage SDK, streamlines integration by automating push notification callback handling within the AppDelegate. The MoEngage SDK enables Method Swizzling by default to offer the quickest and most straightforward initial integration experience.
To ensure maximum compatibility in applications that use other push-enabled SDKs, MoEngage provides a manual forwarding method. If you encounter conflicts, MoEngage recommends disabling the default Method Swizzling in the SDK settings.
This manual approach requires you to forward the push payload directly to our SDK, giving you explicit control and ensuring that critical features like foreground notifications, click tracking, and deep links function reliably across all services.
To resolve conflicts, disable MoEngage's App Delegate Swizzling and manually forward push notification callbacks to the SDK.
1. **Disable Swizzling**: Add the following entry to your `Info.plist` file:
```xml Info.plist wrap theme={null}
MoEngageAppDelegateProxyEnabled
false
```
2. **Manual forwarding in AppDelegate**: After disabling swizzling, implement the following code within your `AppDelegate.swift` file to manually pass notification events to the MoEngage SDK:
```swift Swift wrap theme={null}
import UIKit
import MoEngageSDK
import UserNotifications
class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterDelegate {
// Set up MoEngage SDK and notification delegates when the app launches.
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// Set the UNUserNotificationCenter delegate to enable handling notification-related events.
UNUserNotificationCenter.current().delegate = self
// Register for push notifications. This prompts the user for permission and gets the device token from Apple.
// Below code you can ignore if you don't want prompt on app launch and want to use provisional push in starting of app and further at some instance in app you can prompt for permission.
MoEngageSDKMessaging.sharedInstance.registerForRemoteNotification(withCategories: nil, andUserNotificationCenterDelegate: self)
// This is for requesting provisional authorization for notifications.
// Use only if MoEngageSDKMessaging.sharedInstance.registerForRemoteNotification() is not used on app launch
MoEngageSDKMessaging.sharedInstance.registerForRemoteProvisionalNotification(withCategories: nil,
andUserNotificationCenterDelegate: self)
return true
}
// Called after a successful push token registration with Apple. Pass the token to MoEngage.
func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
// Essential: Pass the device token to MoEngage to enable sending push campaigns.
MoEngageSDKMessaging.sharedInstance.setPushToken(deviceToken)
}
// Handles user interaction with a notification (e.g., a tap).
func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void) {
// Forward the notification response to MoEngage for automatic click tracking, deep linking, and rich landing page handling.
MoEngageSDKMessaging.sharedInstance.userNotificationCenter(center, didReceive: response)
completionHandler()
}
// Handles notifications that are received while the app is in the foreground.
func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) {
// Decide how the notification should be presented in the foreground (e.g., with a banner and sound).
if #available(iOS 14.0, *) {
completionHandler([.sound, .badge, .banner, .list])
} else {
completionHandler([.alert, .sound, .badge])
}
}
// This method is called if push token registration fails.
func application(_ application: UIApplication, didFailToRegisterForRemoteNotificationsWithError error: Error) {
// Forward the registration failure to the MoEngage SDK. This helps in tracking and diagnosing push delivery issues.
MoEngageSDKMessaging.sharedInstance.didFailToRegisterForPush()
}
```
```objective-c Objective C wrap theme={null}
#import
#import
#import
@interface AppDelegate : UIResponder
@end
@implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Other app setup code...
[UNUserNotificationCenter currentNotificationCenter].delegate = self;
[[MoEngageSDKMessaging sharedInstance] registerForRemoteNotificationWithCategories:nil andUserNotificationCenterDelegate:self];
return YES;
}
// Forward the device token
- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken {
[[MoEngageSDKMessaging sharedInstance] setPushToken:deviceToken];
// Forward to other SDKs if necessary
}
// Forward notification clicks/responses
- (void)userNotificationCenter:(UNUserNotificationCenter *)center
didReceiveNotificationResponse:(UNNotificationResponse *)response
withCompletionHandler:(void (^)(void))completionHandler {
[[MoEngageSDKMessaging sharedInstance] userNotificationCenter:center didReceive:response];
// Forward to other SDKs if necessary
completionHandler();
}
- (void)userNotificationCenter:(UNUserNotificationCenter *)center
willPresentNotification:(UNNotification *)notification
withCompletionHandler:(void (^)(UNNotificationPresentationOptions))completionHandler {
if (@available(iOS 14.0, *)) {
completionHandler(UNNotificationPresentationOptionBanner | UNNotificationPresentationOptionList | UNNotificationPresentationOptionSound | UNNotificationPresentationOptionBadge);
} else {
completionHandler(UNNotificationPresentationOptionAlert | UNNotificationPresentationOptionBadge | UNNotificationPresentationOptionSound);
}
}
@end
```
## Step 3: Validate Notifications
To ensure your MoEngage iOS Push Notification integration is fully functional, perform the following validation steps:
### Verify Dashboard Configuration
Before sending a test notification, first verify the integration status on your MoEngage dashboard. Navigate to the iOS Push section of your profile and confirm the following:
* The integration status icon is green.
* For provisional authorization, the **Opt-In Status** is *Unknown*.
* For standard permission, the **Opt-In Status** is *True*.
For more information, refer to [iOS Push Notifications Integration Validation](https://www.moengage.com/docs/user-guide/getting-started/integration-validation/ios-push-notifications-integration-validation).
### Test Notification Delivery and Display
Send a Test Notification: From your MoEngage dashboard, create and send a test push notification that includes rich media (e.g., an image, GIF, or video).
* **Observe Device Behavior**:
* **Foreground**: When your application is active (in the foreground), the notification should be displayed as a banner and accompanied by a sound.
* **Background**: If your application is in the background, the notification should still be delivered to the device and appear in the notification center.
* **Killed State**: Even when your application is force-closed or not running, the notification must be delivered and visible in the notification center.
* **Rich Media Rendering**: Verify that the rich media content within the notification (for example, the image) renders correctly across all app states (foreground, background, killed).
### Tracking and Analytics Verification
* **Access Campaign Analytics**: Navigate to the analytics section of the push notification campaign you just sent within the MoEngage UI.
* **Validate Impressions**: Confirm that the "Impressions" metric for your campaign is incrementing. This indicates that the MoEngage SDK is successfully tracking when notifications are delivered and displayed on the device.
You have successfully integrated MoEngage iOS Push Notifications, encompassing:
* **Standard and Provisional Push Registration**: Correct handling of user consent for push notifications.
* **Reliable Delivery**: Notifications are delivered and tracked in all application states: foreground, background, and killed.
* **Rich Media Support**: Enhanced notification experiences via the NSE, allowing for visually engaging content.
* **Comprehensive Tracking**: Accurate click and impression tracking across all app states, facilitated by the NSE, providing valuable campaign performance data.
## Optional
After basic push notifications are implemented, enhance them with the following features.
### Push Templates
Create interactive, visually rich notifications by implementing two app extensions:
* **Notification Service Extension**: Intercepts and modifies the notification payload before it is displayed. Use this to download and attach rich media, such as images or GIFs.
* [**Notification Content Extension**](https://www.moengage.com/docs/developer-guide/ios-sdk/push/optional/push-templates): Renders a custom UI for the notification. Use this to create interactive experiences, such as image carousels or custom layouts.
### Actionable Buttons
Embed buttons within a notification to allow users to perform tasks directly from the notification interface, such as "Reply" or "Archive." For more information, refer [here](https://www.moengage.com/docs/developer-guide/ios-sdk/push/basic/actionable-notifications).
### Configure Push Notification Badge Behavior
With `disableBadgeReset(true)` enabled, the SDK won't reset the badge to 0 on app launch. Instead, clicking a notification decrements the badge by 1, maintaining accurate counts when multiple notifications are present. Add the code snippet below post SDK initialization in `AppDelegate.swift` file:
```swift Swift wrap theme={null}
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
MoEngageSDKMessaging.sharedInstance.disableBadgeReset(true)
return true
}
```
```objective-c Objective C wrap theme={null}
[[MoEngageSDKMessaging sharedInstance] disableBadgeReset:YES];
```
## Best Practices
Adhering to these best practices will ensure a robust and reliable push notification experience for your users and accurate data for your campaigns.
* **Test across all app states (Foreground, Background, Killed)**: This ensures consistent notification delivery, correct rich media rendering, and accurate tracking regardless of how the user is interacting with their device.
* **If using provisional push, verify settings**: Confirm the device's notification settings show *Deliver Quietly* initially. Request the standard push permission and allow it. Then, go to **Settings** to confirm that the permission status is *Authorized*.
* **Validate rich media URLs**: Always check image/video URLs to prevent broken media displays and ensure the NSE can successfully attach content to your notifications.
## FAQs
These symptoms typically indicate a "swizzling conflict" in your AppDelegate. This can happen if another third-party SDK is also managing push notification callbacks. To resolve this, you can disable MoEngage's automatic swizzling and implement manual forwarding instead. Refer to the detailed instructions for the correct implementation.
This issue is commonly caused by an incorrect configuration of the NSE. Verify these three key areas:
* **NSE Target Configuration:** Ensure the `MoEngageNotificationService` target is correctly added to your project and is properly configured.
* **App Group ID:** Confirm that the same App Group ID is enabled for both your main app and the NSE target, and that it has been correctly set in your code.
* **Media URL Reachability:** Check that the URLs for your images, GIFs, or videos are public and accessible. A broken URL will prevent the media from being downloaded and displayed.
* **Transport, Format, and Size:** Check that the media is served over HTTPS, is within Apple's size limit for that media type, and uses a format that iOS supports. Refer to [Media Requirements](#media-requirements).
* Ensure the deployment target of the extension matches the main app.
Verify the Bundle ID in Xcode matches the MoEngage UI. Ensure the APNs environment (Development or Production) matches the uploaded certificate type. Confirm your APNs certificate or auth key is valid. Test on a physical device; push notifications do not work on simulators.
Enable the Push Notifications capability in Xcode. Check for a stable internet connection and ensure no MDM or VPN profiles are blocking APNs ports. Verify another SDK is not intercepting APNs delegate callbacks.
Avoid requesting both standard and provisional authorization at launch. In the device's Settings for your app, confirm "Deliver Quietly" is not enabled. By design, provisional notifications are delivered silently to the Notification Center without an alert or sound.
# Push Notifications
Source: https://moengage.com/docs/developer-guide/ios-sdk/push/basic/push-notifications
Set up push notifications in your iOS app with MoEngage including APNS, templates, and geofencing.
Push Notifications are a great way to keep your users engaged and informed about your app. You can reach your app users quickly and effectively. This guide will help you through the setup process for using MoEngage SDK to send push notifications.
Follow these steps for setting up your app for Push Notifications :
* [Create an APNS Certificate for your app in .pem format and upload it to iOS Push Settings](/docs/developer-guide/ios-sdk/push/basic/apns-certificate-pem-file-legacy)
* [Add support for custom push templates](/docs/developer-guide/ios-sdk/push/optional/push-templates)
* [Make changes for supporting Actionable Notifications](/docs/developer-guide/ios-sdk/push/basic/actionable-notifications)
* [Add Notification center to show all or filtered list of notifications to your users](/docs/developer-guide/ios-sdk/push/optional/i-os-notification-center)
* [Support Geofence based push notifications in your app](/docs/developer-guide/ios-sdk/push/optional/location-triggered)
# Broadcast Live Activity
Source: https://moengage.com/docs/developer-guide/ios-sdk/push/optional/broadcast-live-activity
Display real-time broadcast updates on the iPhone Lock Screen using MoEngage Broadcast Live Activities.
# Overview
[iOS Live Activities](https://developer.apple.com/design/human-interface-guidelines/live-activities) display your app's most current data as real-time, interactive updates on the iPhone Lock Screen and in the [Dynamic Island](https://support.apple.com/en-in/guide/iphone/iph28f50d10d/ios). This allows users to track events like sports scores, order status, or flight updates without opening your app, significantly boosting engagement and user experience.
**Information**
Live Activities and push notifications have different user permission models. By default, Live Activities are enabled for an app. Users can manage permissions for each app individually in their device settings.
**Prerequisites**
Before you begin, ensure your project and accounts are configured correctly.
1. **Apple Developer Account Configuration**:
* In your Apple Developer account, navigate to **Certificates**, **IDs & Profiles** > **Identifiers** and select your app's identifier.
* Under the **Capabilities** tab, ensure that **Push Notifications** and **Broadcast Capability** checkboxes are selected. This is mandatory for the Apple Push Notification service (APNs) to deliver activity updates.
* **APNs Authentication Key**: To authorize MoEngage to send push notifications on your behalf, you must configure an APNs Authentication Key. For detailed steps on how to upload the .p8 file to the MoEngage dashboard, please refer to the [documentation](https://www.moengage.com/docs/developer-guide/ios-sdk/push/basic/apns-authentication-key) on APNs Authentication Key
2. **Xcode and iOS Version**:
* **Xcode**: Use Xcode 14.1 or later.
* **iOS Target**: Your Live Activity must target iOS 18 and later.
# Implementing a Live Activity
This section covers the client-side setup required within your Xcode project.
## Step 1: Add a Widget Extension
1. In Xcode, navigate to **File** > **New** > **Target**.
2. Select **Widget Extension** and click **Next**.
3. Enter a product name for your widget.
4. Select the **Include Live Activities** checkbox.
5. Click **Finish**.
## Step 2: Configure App's Info.plist
Add Live Activities support to your main app's Info.plist.
```xml XML wrap theme={null}
NSSupportsLiveActivities
```
## Step 3: MoEngageLiveActivity integration
**Information**
To integrate the MoEngageLiveActivity framework, ensure you are using the appropriate MoEngage-iOS-SDK version.
#### **Install using Swift Package Manager (Recommended )**
MoEngageLiveActivity framework is supported through SPM from SDK version 10.02.1 To integrate, use the GitHub url [https://github.com/moengage/apple-sdk.git](https://github.com/moengage/apple-sdk.git) and set the branch as master or required version.
#### **Install using CocoaPod**
**Information**
CocoaPods is being deprecated. MoEngage recommends using Swift Package Manager for all new integrations. For more info on CocoaPods, refer to [CocoaPods Integration Guide](https://www.moengage.com/docs/developer-guide/ios-sdk/sdk-integration/basic/integration-through-cocoa-pods).
To integrate the MoEngageLiveActivity framework, add the following dependency to your Podfile:
```ruby Ruby wrap theme={null}
target 'MoETest' do
use_frameworks!
# Pods for app target
pod 'MoEngage-iOS-SDK' # specify version constraint
pod 'MoEngage-iOS-SDK/LiveActivity'
target 'LiveActivity' do
use_frameworks!
inherit! :search_paths
# Pods for live activity extension target
pod 'MoEngage-iOS-SDK/LiveActivity'
end
end
```
## Step 4: Define the Live Activity Attributes
In the Swift file generated for your widget extension, define the data structure for your Live Activity.
1. Configure ActivityAttributes: Create a struct that conforms to [ActivityAttributes](https://developer.apple.com/documentation/activitykit/activityattributes). This struct will contain:
* **Static Data**: Attributes that are set once and do not change.
* **ContentState**: A nested struct for dynamic data that will be updated in real-time.
```swift Swift wrap theme={null}
import Foundation
import ActivityKit
import WidgetKit
import SwiftUI
struct FootballActivityAttributes: ActivityAttributes {
public struct ContentState: Codable, Hashable {
// Dynamic stateful properties about your activity go here!
var teamOneScore: Int
var teamTwoScore: Int
}
// Fixed non-changing properties about your activity go here!
var team1Name: String
var team2Name: String
}
```
2. When creating ActivityConfiguration, use *MoEngageActivityAttributes\* instead of FootballActivityAttributes as your ActivityAttributes type.
3. Track widget clicks by configuring the deeplink and widget ID with the moengageWidgetClickURL API.
```swift Swift wrap theme={null}
import ActivityKit
import WidgetKit
import SwiftUI
import MoEngageLiveActivity
struct FootballActivityWidget: Widget {
var body: some WidgetConfiguration {
ActivityConfiguration(for: MoEngageActivityAttributes.self) { context in
// Lock screen/banner UI goes here
VStack(spacing: 12) {
// UI elements
}
.moengageWidgetClickURL(URL(string: "moeapp://game"), context: context, widgetId: 2)
} dynamicIsland: { context in
DynamicIsland {
// Expanded UI goes here
} compactLeading: {
// Compact Leading UI goes here
} compactTrailing: {
// Compact Trailing UI goes here
} minimal: {
// Minimal UI goes here
}
.moengageWidgetClickURL(URL(string: "moeapp://game"), context: context, widgetId: 1)
}
}
}
```
**Information**
MoEngage recommends that you acquaint yourself with Apple's Live Activities [prerequisites and limitations](https://developer.apple.com/documentation/activitykit/displaying-live-data-with-live-activities#Understand-constraints), as these are distinct from those of MoEngage.
4. **Ensure Target Membership**: Make your ActivityAttributes struct accessible to your main app target.
1. Select the Swift file where you defined your ActivityAttributes.
2. Open the **File Inspector** (Option + Command + 1).
3. In the **Target Membership** section, check the box for your main app target.
# Managing the Live Activity Lifecycle
Once your app is configured, you can start, update, and end Live Activities using a combination of local app code and MoEngage APIs.
## Step 5: Create a Live Activity Campaign (One-Time Setup)
Before you can start a Live Activity, you must first [create a campaign](https://www.moengage.com/docs/user-guide/campaigns-and-channels/mobile-push/create/create-push-campaigns) on the MoEngage platform. This one-time API call defines the campaign's properties, such as the target audience and conversion goals.
Upon successful creation, the API returns a channel\_id. This ID is essential for two reasons:
* It identifies the campaign you want to start remotely for a target segment.
* It allows users outside the original target segment to start the same Live Activity locally from within your app (e.g., by tapping a button).
For more information, refer [here](https://www.moengage.com/docs/api/create-campaigns/create-campaign).
**Success/Developer Info**
Your server should store the returned channel\_id, MoEngage metadata, and make it available to your mobile app. Your app will need this data to initiate the Live Activity locally.
## Step 6: Start a Live Activity
A Live Activity instance can be started remotely via a push or locally from the app.
**Live Activity tracking validations**
* **Track before initialization.** Live Activity tracking methods must be called only once the SDK is initialized. Calling them earlier throws a fatal exception and crashes the app in `DEBUG` builds. In Release and TestFlight builds, the call is dropped silently and logged.
* **Duplicate `trackStarted`.** Calling `MoEngageSDKLiveActivity.trackStarted` more than once for the same Live Activity throws a fatal exception in `DEBUG` builds. In Release and TestFlight builds, the duplicate call is ignored.
* If you are upgrading an existing app and these strict `DEBUG` validations cause disruptive crashes while you refactor your tracking code, you can temporarily opt out by calling `disableIntegrationValidator()` during SDK initialization. Use this strictly as a stopgap for phased upgrades, and aim to remove the opt-out once your attribute call sites are properly validated.
### Push-to-Start (Remote)
Start an activity for your defined audience using a push notification. For more information, refer [here](https://www.moengage.com/docs/api/live-activities/start-broadcast-live-activity).
### Click-to-Start (Local)
Start an activity from within the app, triggered by a user action.
Get Live Activity data from the [createAttributes(withCampaign:completion:)](https://moengage.github.io/ios-api-reference/Enums/MoEngageSDKLiveActivity.html#/s:20MoEngageLiveActivity0ab7SDKLiveD0O16createAttributes12withCampaign4file0J2Id6method4line6column10completionyAC0I0Vy_xG_s12StaticStringVA2PS2uyAM6ResultVy_x_GSgScMYcct0D3Kit0dG0RzlFZ) or [createAttributes(withCampaign:) async](https://moengage.github.io/ios-api-reference/Enums/MoEngageSDKLiveActivity.html#/s:20MoEngageLiveActivity0ab7SDKLiveD0O16createAttributes12withCampaign4file0J2Id6method4line6columnAC0I0V6ResultVy_x_GSgALy_xG_s12StaticStringVA2SS2utYa0D3Kit0dG0RzlFZ) SDK APIs by combining your application's ActivityAttributes data with mandatory MoEngage metadata (retrieved from your server). Use Apple's Activity.request() method with channel\_id (retrieved from your server) to start Live Activity. This links the locally started activity to your campaign.
**Information**
Once Live Activity is started, the Live Activity Started event needs to be tracked using the [trackStarted(activity:)](https://moengage.github.io/ios-api-reference/Enums/MoEngageSDKLiveActivity.html#/s:20MoEngageLiveActivity0ab7SDKLiveD0O12trackStarted8activity4file0I2Id6method4line6columny0D3Kit0D0CyAA0abD10AttributesVyxGG_s12StaticStringVA2SS2utAK0dO0RzlFZ) API.
```swift Swift wrap theme={null}
import MoEngageLiveActivity
guard #available(iOS 18, *) else { return }
guard let result = await MoEngageSDKLiveActivity.createAttributes(
withCampaign: MoEngageSDKLiveActivity.Campaign(
campaignId: "Your Campaign Id", campaignName: "Your Campaign Name",
deliveryType: "Broadcast Live Activity",
attributeType: "\(FootballActivityAttributes.self)",
instanceId: "Your Instance Id",
appAttributes: FootballActivityAttributes(team1Name: "Chiefs", team2Name: "Bills"),
appContent: FootballActivityAttributes.ContentState(teamOneScore: 0, teamTwoScore: 0)
)
) else { return }
do {
let activity = try MoEngageActivity.request(
attributes: result.attributes,
content: .init(
state: result.content, staleDate: .distantFuture,
relevanceScore: 10
),
pushType: .channel("Your channel Id"), style: .standard
)
// Track started event
MoEngageSDKLiveActivity.trackStarted(activity: activity)
} catch {
// log error
}
```
## Step 7: Update a Live Activity
Update an activity for your defined audience using a push notification. For more information, refer [here](https://www.moengage.com/docs/api/live-activities/update-broadcast-live-activity).
## Step 8: End a Live Activity
A Live Activity can end through user dismissal, a system timeout, or a command from your server. For more information, refer [here](https://www.moengage.com/docs/api/live-activities/end-broadcast-live-activity).
# iOS Notification Center
Source: https://moengage.com/docs/developer-guide/ios-sdk/push/optional/i-os-notification-center
Add an inbox view controller to your iOS app to display read and unread push notifications.
Inbox is a drop-in view controller which contains the read and unread push notifications. Even if the user has not clicked on a notification, it will be present in the Inbox and will be highlighted to signify it is unread status. The title and the look and feel of the view are also customisable.
Inbox view controller is added as a child view controller to your own controller. This helps you get the delegate callback in the same controller, which you can further use for navigation to different screens.
# SDK Installation
## Install using Swift Package Manager
MoEngageInbox is supported through SPM from SDK version 1.2.0. To integrate, use the GitHub url [https://github.com/moengage/apple-sdk.git](https://github.com/moengage/apple-sdk.git) for SDK versions equal and above 9.23.0, or [https://github.com/moengage/MoEngage-iOS-SDK.git](https://github.com/moengage/MoEngage-iOS-SDK.git) for other SDK versions link and set the branch as master or required version.
## Install using CocoaPod
**Information**
CocoaPods is being deprecated. MoEngage recommends using Swift Package Manager for all new integrations. For more info on CocoaPods, refer to [CocoaPods Integration Guide](/docs/developer-guide/ios-sdk/sdk-integration/basic/integration-through-cocoa-pods).
From MoEngage-iOS-SDK version 8.2.0 ,Inbox module is separated from the SDK to a separate module as MoEngageInbox and hence has to be added separately.
Integrate MoEngageInbox framework by adding the dependency in the podfile as show below.
```ruby Ruby wrap theme={null}
pod 'MoEngage-iOS-SDK/Inbox',
```
Now run `pod install` to install the framework
# Inbox Setup Checklist
Make sure the following items are implemented before using the Inbox Module:
1. Update the MoEngage-iOS-SDK to version >= 9.0.0
2. Integrate the MoEngageInbox module of version >= 2.0.0
3. [Implement Notification Service Extension](/docs/developer-guide/ios-sdk/push/basic/ios-push-integration-tutorial#tracking-and-analytics-verification) and Integrate [MoEngageRichNotifcation](https://cocoapods.org/pods/MORichNotification)(>= 7.0.0).
4. AppGroupID is set in **App Target** [Capabilities](/docs/developer-guide/ios-sdk/push/basic/ios-push-integration-tutorial#step-1-implement-notification-service-extension-nse) and the [same is passed to the SDK](/docs/developer-guide/ios-sdk/push/basic/ios-push-integration-tutorial#step-1-implement-notification-service-extension-nse).
5. AppGroupID is set in **Notification Service Extension Target** [Capabilities](/docs/developer-guide/ios-sdk/push/basic/ios-push-integration-tutorial#step-1-implement-notification-service-extension-nse) and the [same is passed to the MoEngageRichNotification SDK](/docs/developer-guide/ios-sdk/push/basic/ios-push-integration-tutorial#step-1-implement-notification-service-extension-nse).
**App Group ID**
Make sure the App Group ID configured for both the **App Target** and the **Notification Service Extension Target** are the **same**.
# How to use Inbox?
1. Import `MoEngageInbox` in your View Controller.
2. Create a property - @property(nonatomic, strong) MoEngageInboxViewController \*inboxController.
3. In viewDidLoad, add the below code to fetch MoEngageInboxViewController
```swift Swift wrap theme={null}
MoEngageSDKInbox.sharedInstance.getInboxViewController(withUIConfiguration: nil, withInboxWithControllerDelegate: nil, forAppID: "YOUR_WORKSPACE_ID") { inboxController in
self.inboxController = inboxController
}
```
```objective-c Objective C wrap theme={null}
[[MoEngageSDKInbox sharedInstance] getInboxViewControllerWithUIConfiguration:nil withInboxWithControllerDelegate:nil forAppID:@"YOUR_WORKSPACE_ID" withCompletionBlock:^(MoEngageInboxViewController * _Nullable) {
self.inboxController = inboxController;
}];
```
# Push/Present the MoEngageInboxViewController
In order for the SDK to handle the transition, use one of the methods.
```swift Swift wrap theme={null}
MoEngageSDKInbox.sharedInstance.pushInboxViewController(toNavigationController: self.navigationController!, withUIConfiguration: nil)
// Present
MoEngageSDKInbox.sharedInstance.presentInboxViewController(withUIConfiguration: nil)
```
```objective-c Objective C wrap theme={null}
[[MoEngageSDKInbox sharedInstance] pushInboxViewControllerToNavigationController:navigationController withUIConfiguration:nil withInboxWithControllerDelegate:nil forAppID:@"YOUR_WORKSPACE_ID"];
// Present
[[MoEngageSDKInbox sharedInstance] presentInboxViewControllerWithUIConfiguration:nil withInboxWithControllerDelegate:nil forAppID:@"YOUR_WORKSPACE_ID"];
```
# MoEngageInboxViewControllerDelegate Methods
Use [*MoEngageInboxViewControllerDelegate*](https://moengage.github.io/ios-api-reference/Protocols/MoEngageInboxViewControllerDelegate.html) protocol for getting the callbacks from the Inbox Module:
```swift Swift wrap theme={null}
extension NotificationsViewController: MoEngageInboxViewControllerDelegate {
//Called when inbox cell is selected
func inboxEntryClicked(_ inboxItem: MoEngageInboxEntry) {
print("Inbox Clicked")
}
//Called when inbox item is deleted
func inboxEntryDeleted(_ inboxItem: MoEngageInboxEntry) {
print("Inbox item deleted")
}
// Called when MoEngageInboxViewController is dismissed after being presented
func inboxViewControllerDismissed() {
print("Dismissed")
}
}
```
```objective-c Objective-C wrap theme={null}
//Called when inbox cell is selected
- (void)inboxEntryClicked:(MoEngageInboxEntry *)inboxItem {
NSLog(@"Inbox item Clicked");
}
//Called when inbox item is deleted
- (void)inboxEntryDeleted:(MoEngageInboxEntry *)inboxItem {
NSLog(@"Inbox item Deleted");
}
// Called when MoEngageInboxViewController is dismissed after being presented
- (void)inboxViewControllerDismissed {
NSLog(@"Inbox Dismissed");
}
```
Set [*MoEngageInboxViewControllerDelegate*](https://moengage.github.io/ios-api-reference/Protocols/MoEngageInboxViewControllerDelegate.html) by passing the delegate as parameter in the below functions.
```swift Swift wrap theme={null}
//Push
MoEngageSDKInbox.sharedInstance.pushInboxViewController(toNavigationController: self.navigationController!, withUIConfiguration: nil, withInboxWithControllerDelegate: self)
//Present
MoEngageSDKInbox.sharedInstance.presentInboxViewController(withUIConfiguration: nil, withInboxWithControllerDelegate: self)
//Fetch MoEngageInboxViewController
MoEngageSDKInbox.sharedInstance.getInboxViewController(withUIConfiguration: nil, withInboxWithControllerDelegate: self, forAppID: "YOUR_WORKSPACE_ID") { inboxController in
self.inboxController = inboxController
}
```
```objective-c Objective C wrap theme={null}
//Push
[[MoEngageSDKInbox sharedInstance] pushInboxViewControllerToNavigationController:navigationController withUIConfiguration:nil withInboxWithControllerDelegate:self forAppID:@"YOUR_WORKSPACE_ID"];
//Present
[[MoEngageSDKInbox sharedInstance] presentInboxViewControllerWithUIConfiguration:nil withInboxWithControllerDelegate:self forAppID:@"YOUR_WORKSPACE_ID"];
//Fetch MoEngageInboxViewController
[[MoEngageSDKInbox sharedInstance] getInboxViewControllerWithUIConfiguration:nil withInboxWithControllerDelegate:self forAppID:@"YOUR_WORKSPACE_ID" withCompletionBlock:^(MoEngageInboxViewController * _Nullable inboxController) {
self.inboxController = inboxController;
}];
}
```
# Customizing Appearance
1. You can push/present your controller. If you push your controller, make sure to add “Done” or “Cancel” button as a UIBarButtonItem to dismiss your View Controller.
2. You can get the delegate callback of the click action on inbox cells.
3. You can use this data for tracking events or navigation to another screen.
4. You can customize the look and feel of the inbox view controller using the method:
```swift Swift wrap theme={null}
let configuration = MoEngageInboxUIConfiguration()
configuration.cellDefaultBackgroundColor = .red
configuration.cellHeaderLabelFont = UIFont.systemFont(ofSize: 15)
configuration.cellMessageLabelFont = UIFont.systemFont(ofSize: 13)
configuration.cellSelectionTintColor = .red
configuration.cellHeaderLabelTextColor = .white
configuration.cellMessageLabelTextColor = .white
configuration.cellUnreadBackgroundColor = .blue
let navigationBarStyle = MoEngageInboxNavigationBarStyle()
navigationBarStyle.navigationBarColor = .black
navigationBarStyle.navigationBarTintColor = .blue
navigationBarStyle.navigationBarTitleColor = .blue
navigationBarStyle.navigationBarTransluscent = false
configuration.navigationBarStyle = navigationBarStyle
//Present
MoEngageSDKInbox.sharedInstance.presentInboxViewController(withUIConfiguration: configuration, forAppID: "YOUR_WORKSPACE_ID")
//Push
MoEngageSDKInbox.sharedInstance.pushInboxViewController(toNavigationController: self.navigationController!, withUIConfiguration: configuration, forAppID: "YOUR_WORKSPACE_ID")
//Fetch MoEngageInboxViewController
MoEngageSDKInbox.sharedInstance.getInboxViewController(withUIConfiguration: configuration, forAppID: "YOUR_WORKSPACE_ID") { controller in
}
```
```objective-c Objective C wrap theme={null}
MoEngageInboxUIConfiguration* configuration = [[MoEngageInboxUIConfiguration alloc] init];
configuration.cellDefaultBackgroundColor = [UIColor redColor];
configuration.cellHeaderLabelFont =[UIFont fontWithName:@"AvenirNext-Bold" size:18];
configuration.cellMessageLabelFont = [UIFont fontWithName:@"AvenirNext-Bold" size:15];
configuration.cellHeaderLabelTextColor = [UIColor whiteColor];
configuration.cellMessageLabelTextColor =[UIColor whiteColor];
configuration.cellUnreadBackgroundColor = [UIColor blueColor];
MoEngageInboxNavigationBarStyle* navigationBarStyle = [[MoEngageInboxNavigationBarStyle alloc] init];
navigationBarStyle.navigationBarColor = [UIColor blackColor];
navigationBarStyle.navigationBarTintColor = [UIColor blueColor];
navigationBarStyle.navigationBarTitleColor = [UIColor blueColor];
navigationBarStyle.navigationBarTransluscent = true;
configuration.navigationBarStyle = navigationBarStyle;
//Present
[[MoEngageSDKInbox sharedInstance] presentInboxViewControllerWithUIConfiguration:configuration withInboxWithControllerDelegate:self forAppID:@"YOUR_WORKSPACE_ID"];
//Push
[[MoEngageSDKInbox sharedInstance] pushInboxViewControllerToNavigationController:navigationController withUIConfiguration:configuration withInboxWithControllerDelegate:nil forAppID:@"YOUR_WORKSPACE_ID"];
//Fetch MoEngageInboxViewController
[[MoEngageSDKInbox sharedInstance] getInboxViewControllerWithUIConfiguration:configuration withInboxWithControllerDelegate:nil forAppID:@"YOUR_WORKSPACE_ID" withCompletionBlock:^(MoEngageInboxViewController * _Nullable inboxController) {
}];
```
# Self Handled Inbox
## Fetch Inbox Messages:
Inbox can be completely customized now. Use [*getInboxMessages(forAppID:withCompletionBlock:)*](https://moengage.github.io/ios-api-reference/Classes/MoEngageSDKInbox.html#/c:@M@MoEngageInbox@objc\(cs\)MoEngageSDKInbox\(im\)getInboxMessagesForAppID:withCompletionBlock:) to fetch the inbox messages.
```swift Swift wrap theme={null}
MoEngageSDKInbox.sharedInstance.getInboxMessages(forAppID: "YOUR_WORKSPACE_ID") { inboxMessages, account in
print("Received Inbox messages")
}
```
```objective-c Objective C wrap theme={null}
[[MoEngageSDKInbox sharedInstance] getInboxMessagesForAppID:@"YOUR_WORKSPACE_ID" withCompletionBlock:^(NSArray * _Nonnull inboxEntry, MoEngageAccountMeta * _Nullable accountMeta) {
NSLog(@"Received Inbox messages");
}];
```
## Mark a notification as Read:
An inbox notification can be marked as read with the method [*markInboxNotificationClicked(withCampaignID:forAppID:completionHandler)*](https://moengage.github.io/ios-api-reference/Classes/MoEngageSDKInbox.html#/c:@M@MoEngageInbox@objc\(cs\)MoEngageSDKInbox\(im\)markInboxNotificationClickedWithCampaignID:forAppID:completionHandler:) by providing the campaign ID of the notification while calling the method. The method will return the updated notification payload where the `isRead` key will be set to true.
```swift Swift wrap theme={null}
//Get the MoEngageInboxEntry instance
let pushModel = inboxMessages[]
if !pushModel.isRead {
MoEngageSDKInbox.sharedInstance.markInboxNotificationClicked(withCampaignID: pushModel.campaignID)
}
```
```objective-c Objective C wrap theme={null}
//An example of marking the inbox message as read
MoEngageInboxEntry *pushDataObj = [self.inboxMessagesArray objectAtIndex:];
if (!pushDataObj.isRead){
[[MoEngageSDKInbox sharedInstance] markInboxNotificationClickedWithCampaignID:pushDataObj.campaignID forAppID:nil completionHandler:nil];
//Rest of the implementation
}
```
## Track Inbox Notification Clicks:
An inbox notification click can be tracked by using method [*trackInboxClick(withCampaignID:forAppID:)*](https://moengage.github.io/ios-api-reference/Classes/MoEngageSDKInbox.html#/c:@M@MoEngageInbox@objc\(cs\)MoEngageSDKInbox\(im\)trackInboxClickWithCampaignID:forAppID:) by providing the campaign ID of the notification while calling the method.
```swift Swift wrap theme={null}
//Get the MoEngageInboxEntry instance
let pushModel = inboxMessages[]
MoEngageSDKInbox.sharedInstance.trackInboxClick(withCampaignID: pushModel.campaignID)
```
```objective-c Objective C wrap theme={null}
//An example of marking the inbox message as read
MoEngageInboxEntry *pushDataObj = [self.inboxMessagesArray objectAtIndex:];
[[MoEngageSDKInbox sharedInstance] trackInboxClickWithCampaignID:pushDataObj.campaignID forAppID:nil];
```
## Process the Inbox Clicks:
If you want to perform the actions supported by the SDK(i.e, rich landing, deep linking, coupon code etc) associated with the notifications on clicking the entry in Inbox call [*processInboxNotification(withCampaignID:forAppID:)*](https://moengage.github.io/ios-api-reference/Classes/MoEngageSDKInbox.html#/c:@M@MoEngageInbox@objc\(cs\)MoEngageSDKInbox\(im\)processInboxNotificationWithCampaignID:forAppID:) method as shown below.
```swift Swift wrap theme={null}
//Get the MoEngageInboxEntry instance
let pushModel = inboxMessages[]
MoEngageSDKInbox.sharedInstance.processInboxNotification(withCampaignID: pushModel.campaignID)
```
```objective-c Objective C wrap theme={null}
//An example of process the notification actions
MoEngageInboxEntry *pushDataObj = [self.inboxMessagesArray objectAtIndex:];
[[MoEngageSDKInbox sharedInstance] processInboxNotificationWithCampaignID:pushDataObj.campaignID forAppID:@"YOUR_WORKSPACE_ID"];
```
## Get Unread Notifications count:
You can obtain the unread notifications count from the Inbox by using [*getUnreadNotificationCount(forAppID:withCompletionBlock:)*](https://moengage.github.io/ios-api-reference/Classes/MoEngageSDKInbox.html#/c:@M@MoEngageInbox@objc\(cs\)MoEngageSDKInbox\(im\)getUnreadNotificationCountForAppID:withCompletionBlock:) method as shown below:
```swift Swift wrap theme={null}
//Get Unread Notifications count
MoEngageSDKInbox.sharedInstance.getUnreadNotificationCount() { count, accountMeta in
print("Unread message count is \(count)")
}
```
```objective-c Objective C wrap theme={null}
//Get Unread Notifications count
[[MoEngageSDKInbox sharedInstance] getUnreadNotificationCountForAppID:@"YOUR_WORKSPACE_ID" withCompletionBlock:^(NSInteger count, MoEngageAccountMeta * _Nullable accountMeta) {
NSLog(@"Fetched unread message Count");
}];
```
## Deleting Messages
Use [*removeInboxMessages(forAppID:completionHandler:)*](https://moengage.github.io/ios-api-reference/Classes/MoEngageSDKInbox.html#/c:@M@MoEngageInbox@objc\(cs\)MoEngageSDKInbox\(im\)removeInboxMessagesForAppID:completionHandler:) method to remove all the messages currently stored in inbox.
```swift Swift wrap theme={null}
MoEngageSDKInbox.sharedInstance.removeInboxMessages { success in
print("Removed all inbox messages \(success)")
}
```
```objective-c Objective C wrap theme={null}
[[MoEngageSDKInbox sharedInstance] removeInboxMessagesForAppID:@"YOUR_WORKSPACE_ID" completionHandler:^(BOOL) {
// Add your code
}];
```
Use [*removeInboxMessage(withCampaignID:forAppID:completionHandler:)*](https://moengage.github.io/ios-api-reference/Classes/MoEngageSDKInbox.html#/c:@M@MoEngageInbox@objc\(cs\)MoEngageSDKInbox\(im\)removeInboxMessageWithCampaignID:forAppID:completionHandler:) method to remove the single message stored in inbox by passing the Campaign ID.
```swift Swift wrap theme={null}
MoEngageSDKInbox.sharedInstance.removeInboxMessage(withCampaignID: "YOUR CAMPAIGN ID")
```
```objective-c Objective C wrap theme={null}
[[MoEngageSDKInbox sharedInstance] removeInboxMessageWithCampaignID:@"CAMPAIGN ID" forAppID:@"YOUR_WORKSPACE_ID"];
```
# Methods deprecated in SDK version 4.4.0
We have revamped the Inbox Module in the SDK version 4.4.0 and following this, we have deprecated `MOInboxPushDataModel` class and use `MOInboxModel` class instances as the model object for notifications. Along with this, we have also deprecated few of the existing methods of `MOInbox` as listed below:
```objective-c Objective C wrap theme={null}
+(NSArray *)getInboxMessages __deprecated_msg("This method is deprecated as the payload structure has changed, this method will be removed in SDK Version 5.0.0. Use getInboxMessagesWithCompletionBlock: instead");
+(void)trackInboxNotificationClickForCampaign:(MOInboxPushDataModel*)campaignObj andIsFirstClick:(BOOL)isFirstClick __deprecated_msg("This method is deprecated as MOInboxPushDataModel Class is depreacted, this method will be removed in SDK Version 5.0.0. Use trackInboxNotificationClickWithCampaignID: instead");
+(void)processInboxNotificationOnClickForCampaign:(MOInboxPushDataModel*)campaignObj __deprecated_msg("This method is deprecated as MOInboxPushDataModel Class is depreacted, this method will be removed in SDK Version 5.0.0. Use processInboxNotificationWithCampaignID: instead");
+(NSMutableDictionary*)markNotificationReadWithCampaignID:(NSString*)cid __deprecated_msg("This method is deprecated as MOInboxPushDataModel Class is depreacted, this method will be removed in SDK Version 5.0.0. Use markInboxNotificationClickedWithCampaignID: instead");
+(void)writeArrayToFile:(NSMutableArray *)anArray __deprecated_msg("Method Deprecated. From SDK Version 5.0.0 you will not be able to change the inbox file content.");
```
These methods will be removed from the SDK version 5.0.0; therefore, make sure you have updated all the Inbox features to use the newer APIs.
**Information**
The MoEngage SDK only synchronizes push data with the Notification Center when the app is launched for the first time or when it returns to the foreground from the background.
# Location triggered
Source: https://moengage.com/docs/developer-guide/ios-sdk/push/optional/location-triggered
Set up geofence-based location-triggered push notifications in your iOS app with the MoEngage SDK.
**Important**
* Starting from **iOS 14.0**, Apple has provided user control to choose the level of precision of location to be shared in App. Now because of this **region monitoring(Geofence feature) will not work in cases where the precise location is disabled by the user**. Refer [link](https://developer.apple.com/videos/play/wwdc2020/10660/) for more info.
* Region monitoring is only supported with **Always authorization**. When-in-use authorization doesn't support this feature. Refer [link](https://developer.apple.com/documentation/corelocation/choosing_the_authorization_level_for_location_services) for more info.
* **Dwell** trigger is **not supported in iOS**, hence the SDK supports only Enter and Exit triggers.
# How to enable Location Triggered?
## Required Permissions:
**Region Monitoring(Geofences) requires Always Authorization and Precise location accuracy to be enabled to work**. Therefore make sure that the app is configured to get these permissions and also it's a good practice to let the user know the context in which these permissions are needed, this will also encourage the user to provide these permissions.
# SDK Installation
## Install using Swift Package Manager
MoEngageGeofence is supported through SPM from SDK version 4.2.0. To integrate, use the GitHub url [https://github.com/moengage/apple-sdk.git](https://github.com/moengage/apple-sdk.git) for SDK versions equal and above 9.23.0, or [https://github.com/moengage/MoEngage-iOS-SDK.git](https://github.com/moengage/MoEngage-iOS-SDK.git) for other SDK versions link and set the branch as master or required version.
## Install using CocoaPod
**Information**
CocoaPods is being deprecated. MoEngage recommends using Swift Package Manager for all new integrations. For more info on CocoaPods, refer to [CocoaPods Integration Guide](https://www.moengage.com/docs/developer-guide/ios-sdk/sdk-integration/basic/integration-through-cocoa-pods).
Integrate the MoEngageGeofence framework by adding the dependency in the podfile as show below.
```ruby Ruby wrap theme={null}
pod 'MoEngage-iOS-SDK/GeoFence',
```
Now run `pod install `to install the framework
## Manual Integration
To integrate the `MoEngageGeofence` SDK manually to your project follow this [doc](/docs/developer-guide/ios-sdk/manual-integration/manual-integration).
**Note**
MOGeofence has been renamed to MoEngageGeofence from version 4.2.0.Do update the podfile and import statement accordingly.
## Start Geofence Monitoring:
After integrating the MoEngageGeofence module call [*startGeofenceMonitoring()*](https://moengage.github.io/ios-api-reference/Classes/MoEngageSDKGeofence.html#/c:@M@MoEngageGeofence@objc\(cs\)MoEngageSDKGeofence\(im\)startGeofenceMonitoring) method to initiate the geofence module. This will fetch the geofences around the current location of the user.
```swift Swift wrap theme={null}
MoEngageSDKGeofence.sharedInstance.startGeofenceMonitoring()
```
```objective-c Objective C wrap theme={null}
[[MoEngageSDKGeofence sharedInstance] startGeofenceMonitoring];
```
Geofence Handler also has callbacks for `didEnterRegion` and `didExitRegion`. You can get these by confirming to the as `MoEngageSDKGeofence.sharedInstance.setGeofenceDelegate(self)`
```swift Swift wrap theme={null}
extension GeofenceViewController: MoEngageGeofenceDelegate {
func geofenceEnterTriggered(withLocationManager locationManager: CLLocationManager?, andRegion region: CLRegion?, forAccountMeta accountMeta: MoEngageAccountMeta) {
print("Geofence Entered")
}
func geofenceExitTriggered(withLocationManager locationManager: CLLocationManager?, andRegion region: CLRegion?, forAccountMeta accountMeta: MoEngageAccountMeta) {
print("Geofence Exited")
}
}
```
```objective-c Objective C wrap theme={null}
@interface MyViewController ()
---
- (void)geofenceEnterTriggeredWithLocationManager:(CLLocationManager *)locationManager andRegion:(CLRegion *)region forAccountMeta:(MoEngageAccountMeta *)accountMeta {
NSLog(@"Geofence Entered");
}
- (void)geofenceExitTriggeredWithLocationManager:(CLLocationManager *)locationManager andRegion:(CLRegion *)region forAccountMeta:(MoEngageAccountMeta *)accountMeta {
NSLog(@"Geofence Exited");
}
```
# Stop Geofence Monitoring:
After version `9.3.0` we have provided support to stop the monitoring of geofences. To stop geofence monitoring call [*stopGeofenceMonitoring()*](https://moengage.github.io/ios-api-reference/Classes/MoEngageSDKGeofence.html#/c:@M@MoEngageGeofence@objc\(cs\)MoEngageSDKGeofence\(im\)stopGeofenceMonitoring) method.
```swift Swift wrap theme={null}
MoEngageSDKGeofence.sharedInstance.stopGeofenceMonitoring()
```
```objective-c Objective C wrap theme={null}
[[MoEngageSDKGeofence sharedInstance] stopGeofenceMonitoring];
```
# Testing Geofencing
First, create a geofencing campaign on your MoEngage dashboard. You can test geofencing in the following ways:
1. On the simulator:
* You can simulate location as shown below.
* Simulate the location for which you have created the campaign on the dashboard. If you get the respective call back (the delegate methods in MoEngageGeofenceHandler), you are good to go. On simulator, you will not receive push notifications.
2. On the device:
* You can simulate location for real device from the bar above the console as shown below:
* You can add a gpx file with the locations configured. A sample gpx file looks like this:
```xml XML wrap theme={null}
CustomName
```
On the device, once you get the delegate callback for entering or exit in a region, a notification will be sent to the device. This happens instantly, but the notification might take up to 10 minutes.
# Geo Notifications
Once you have received the notification, to identify geo notifications, there is a custom param “cType” = “geo” in the param app\_extra, as shown below:
```json JSON wrap theme={null}
{
"app_extra" = {
cType = geo;
screenData = {
"" = "";
};
screenName = "";
};
aps = {
alert = "exit london- single fence";
badge = 1;
};
moengage = {
cid = "55a628bcf4c4073bb66a368b_GEO:55a628bcf4c4073bb66a368c_ABab1:2015-07-15_14:11:33.704706";
};
}
```
# Push Handled by Application
Source: https://moengage.com/docs/developer-guide/ios-sdk/push/optional/push-handled-by-application
Handle push notification display and tracking yourself using the MoEngage iOS SDK messaging APIs.
**SDK Version**
* **General Messaging:** Supported from version 9.13.0
* **Background Updates (Self handled):** Supported from version 10.10.0
## Track Notification Received
Call SDK's ***logNotificationReceived(withPayload: )*** function to track notification received impression as shown below.
If you have configured a [Notification Service Extension](/docs/developer-guide/ios-sdk/push/basic/ios-push-integration-tutorial#step-1-implement-notification-service-extension-nse), the SDK tracks impressions from the extension for all display notifications, both text-only and rich. You don't need to call `logNotificationReceived(withPayload:)` for these notifications.
Silent Background Update payloads don't go through the extension, so you must call this method explicitly. Refer to [Handling Background Updates (Self-Handled)](#handling-background-updates-self-handled).
```swift Swift wrap theme={null}
MoEngageSDKMessaging.sharedInstance.logNotificationReceived(withPayload:notification.request.content.userInfo) {
// updated content in contentHandler here
}
```
```objective-c Objective C wrap theme={null}
[[MoEngageSDKMessaging sharedInstance] logNotificationReceivedWithPayload:notification.request.content.userInfo completion: ^(void) {
// updated content in contentHandler here
}];
```
## Track Notification Click
To track the notification clicked event using the payload, call the SDK's ***logNotificationClicked*** function, as shown below.
```swift Swift wrap theme={null}
MoEngageSDKMessaging.sharedInstance.logNotificationClicked(withPayload: notification.request.content.userInfo)
```
```objective-c Objective C wrap theme={null}
[[MoEngageSDKMessaging sharedInstance] logNotificationClickedWithPayload:notification.request.content.userInfo];
```
To accurately track clicks or dismissals, use the ***logNotificationClicked(withResponse: )*** method.
```swift Swift wrap theme={null}
MoEngageSDKMessaging.sharedInstance.logNotificationClicked(withResponse: UNNotificationResponse)
```
**Note**
To use above functions, Appdelegate swizzling should be disabled. To see how to disable swizzling, please see the [link](https://www.moengage.com/docs/developer-guide/ios-sdk/push/basic/ios-push-integration-tutorial#app-delegate-method-swizzling).
## Validate if the notification belongs to MoEngage
Call SDK's ***isPushFromMoEngage(withPayload:)*** function to validate if the notification belongs to MoEngage as shown below:
```swift Swift wrap theme={null}
let isPushFromMoEngage = MoEngageSDKMessaging.sharedInstance.isPushFromMoEngage(withPayload: notification.request.content.userInfo))
```
```objective-c Objective C wrap theme={null}
BOOL isPushFromMoEngage = [[MoEngageSDKMessaging sharedInstance] isPushFromMoEngageWithPayload:notification.request.content.userInfo];
```
## Handling Background Updates (Self-Handled)
**Prerequisites for iOS Background Updates**
* **Background Modes:** You must enable **Remote notifications** under the *Signing & Capabilities* tab in Xcode.
* **Payload Identifier:** The SDK identifies these payloads by checking for `nt: sh_b` inside the `moeFeatures` dictionary.
The **Background Update** template allows you to send silent data payloads to your application. Because these notifications do not display a UI, the MoEngage SDK provides a specific helper method to identify them so you can execute custom background logic.
For more information on the payload structure and available keys, refer to [Background Update Templates](https://www.moengage.com/docs/user-guide/campaigns-and-channels/mobile-push/create/push-templates#background-update).
**Implementation Note**
Background updates must be handled in the `didReceiveRemoteNotification` fetch completion handler. You must also call `logNotificationReceived` manually to ensure these silent events are recorded in your analytics.
```swift Swift wrap theme={null}
func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable: Any], fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) {
// 1. Check if it's a MoEngage Background Update payload
if MoEngageSDKMessaging.sharedInstance.isSelfHandledBackgroundNotification(payload: userInfo) {
// 2. Log notification received impression manually
MoEngageSDKMessaging.sharedInstance.logNotificationReceived(withPayload: userInfo) {
// 3. Execute your custom background logic here (e.g., sync data, logout)
// self.handleCustomBackgroundLogic(userInfo)
// 4. Always call the completion handler
completionHandler(.newData)
}
} else {
// Handle standard MoEngage or other push notifications
MoEngageSDKMessaging.sharedInstance.didReceieveNotification(inApplication: application, withInfo: userInfo)
}
}
```
```objective-c Objective C wrap theme={null}
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler {
// 1. Check if it's a MoEngage Background Update payload
if ([[MoEngageSDKMessaging sharedInstance] isSelfHandledBackgroundNotificationWithPayload:userInfo]) {
// 2. Log notification received impression manually
[[MoEngageSDKMessaging sharedInstance] logNotificationReceivedWithPayload:userInfo completion:^{
// 3. Execute your custom background logic here (e.g., sync data, logout)
// [self handleCustomBackgroundLogic:userInfo];
// 4. Always call the completion handler
completionHandler(UIBackgroundFetchResultNewData);
}];
} else {
// Handle standard MoEngage or other push notifications
[[MoEngageSDKMessaging sharedInstance] didReceieveNotificationInApplication:application withInfo:userInfo];
}
}
```
# Push templates
Source: https://moengage.com/docs/developer-guide/ios-sdk/push/optional/push-templates
Add custom push notification templates with Notification Content Extension in your iOS app.
Notification Content Extension allows you to customize the appearance of the notification in expanded mode. For info on how to create campaigns with templates in the dashboard refer to [this](https://www.moengage.com/docs/user-guide/campaigns-and-channels/mobile-push/create/push-templates) link.
**Prerequisites**
Make sure you have completed the [App Target](https://www.moengage.com/docs/developer-guide/ios-sdk/push/basic/ios-push-integration-tutorial#step-1-implement-notification-service-extension-nse) and [Notification Service Extension](https://www.moengage.com/docs/developer-guide/ios-sdk/push/basic/ios-push-integration-tutorial#Optional) Implementation for supporting Rich Push in your project before proceeding with the below steps.
The Notification Service Extension downloads the image, audio, or video used in a template. Refer to [Media Requirements](/docs/developer-guide/ios-sdk/push/basic/ios-push-integration-tutorial#media-requirements) for the transport, format, and size requirements.
# STEPS:
To support these custom push templates, your project needs to have a Notification Content Extension. Follow the below steps to create a Content Extension and to set it up to support MoEngage templates:
## 1. Create a Notification Content Extension
After the target is created, Activate the scheme for Extension when prompted for the same.
After this, your extension will be added to the project you will see a class with the extension name provided by you while creating and an .plist file associated with it.
## 2. Set deployment target
Set the deployment target same as the main app target.
## 3. Add required Capabilities
In the Capabilities Section add **App Groups** and select the same app group id that you have configured in your App target and Notification Service Extension target.
Refer to the **Set AppGroup ID** section of the [doc](https://www.moengage.com/docs/developer-guide/ios-sdk/push/basic/ios-push-integration-tutorial#step-1-implement-notification-service-extension-nse) for more information on setting the app group ID on the main target
## 4. Info.plist changes
Make the changes in the `Info.plist` of your Notification Content Extension, as shown above, set NSExtensionAttributes as following:
| Attribute | Attribute Value |
| :--------------------------------------------- | :------------------ |
| UNNotificationExtensionCategory | MOE\_PUSH\_TEMPLATE |
| UNNotificationExtensionInitialContentSizeRatio | 1.2 |
| UNNotificationExtensionDefaultContentHidden | YES |
| UNNotificationExtensionUserInteractionEnabled | YES |
**Note**
Update the UNNotificationExtensionCategory with the necessary values according to the categories that you have declared.
## 5. Storyboard changes
Select ***MainInterface.storyboard*** in your Content extension and remove the default label which is placed there and set the background color of the view to clear color, as shown:
## 6. MoEngageRichNotification Integration
### Integration via Swift Package Manager (Recommended)
To integrate via SPM, use the GitHub url [https://github.com/moengage/apple-sdk.git](https://github.com/moengage/apple-sdk.git) for SDK versions equal and above 9.23.0, or [https://github.com/moengage/MoEngage-iOS-SDK.git](https://github.com/moengage/MoEngage-iOS-SDK.git) for other SDK versions link and set the branch as master or required version.
### Integration via CocoaPod
**Information**
CocoaPods is being deprecated. MoEngage recommends using Swift Package Manager for all new integrations. For more info on CocoaPods, refer to [CocoaPods Integration Guide](https://www.moengage.com/docs/developer-guide/ios-sdk/sdk-integration/basic/integration-through-cocoa-pods).
For integrating through CocoaPod, include **MoEngageRichNotification** pod for your Notification Content Extension as shown below, and run the pod update / install command:
```ruby Ruby wrap theme={null}
target "NotificationContent" do
pod 'MoEngage-iOS-SDK/RichNotification',
end
```
**Manual Integration**
* To integrate the MoEngageRichNotification SDK manually to your project follow this [doc](https://www.moengage.com/docs/developer-guide/ios-sdk/manual-integration/manual-integration).
* Add MoEngageRichNotification to embed binaries in the App target, and ensure it is linked to your Notification Content Extension target.
## 7. Code Changes in Content Extension:
```swift Swift wrap theme={null}
import UIKit
import UserNotifications
import UserNotificationsUI
import MoEngageRichNotification
class NotificationViewController: UIViewController, UNNotificationContentExtension {
override func viewDidLoad() {
super.viewDidLoad()
// Set App Group ID
MoEngageSDKRichNotification.setAppGroupID("Your App Group ID")
}
func didReceive(_ notification: UNNotification) {
// Method to add template to UI
MoEngageSDKRichNotification.addPushTemplate(toController: self, withNotification: notification)
}
}
```
```objective-c Objective C wrap theme={null}
#import "NotificationViewController.h"
#import
#import
@import MoEngageRichNotification;
@interface NotificationViewController ()
@end
@implementation NotificationViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Set App Group ID
[MoEngageSDKRichNotification setAppGroupID:@"Your App Group ID"];
}
- (void)didReceiveNotification:(UNNotification *)notification {
// Method to add template to UI
[MoEngageSDKRichNotification addPushTemplateToController:self withNotification:notification];
}
@end
```
As shown above, make these changes in your ***NotificationViewController*** class:
1. Set the same App Group ID in ***viewDidLoad()*** method which was enabled in Capabilities.
2. Call [*addPushTemplate(toController:withNotification:)* ](https://moengage.github.io/ios-api-reference/Classes/MoEngageSDKRichNotification.html#/c:@M@MoEngageRichNotification@objc\(cs\)MoEngageSDKRichNotification\(cm\)addPushTemplateToController:withNotification:)method to add template in ***didReceiveNotification()*** callback.
## 8. Notification Click callback in App:
In the case of Simple Image Carousel notification, to know which slide was clicked by the user, make use of `MOMessagingDelegate` to get `notificationClicked(withScreenName: andKVPairs:)` callback to get key-value pairs and screen name if set for the clicked slide. Refer to the example below, here we are registering for the callback in AppDelegate:
```swift Swift wrap theme={null}
class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterDelegate, MoEngageMessagingDelegate{
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// Set the delegate
MoEngageSDKMessaging.sharedInstance.setMessagingDelegate(self)
//Rest of the implementation
}
// Notification Clicked Callback
func notificationClicked(withScreenName screenName: String?, andKVPairs kvPairs: [AnyHashable : Any]?) {
if let screenName = screenName {
print("Navigate to Screen:\(screenName)")
}
if let actionKVPairs = kvPairs {
print("Selected Action KVPair:\(actionKVPairs)")
}
}
// Notification Clicked Callback with Push Payload
func notificationClicked(withScreenName screenName: String?, kvPairs: [AnyHashable : Any]?, andPushPayload userInfo: [AnyHashable : Any]) {
print("Push Payload: \(userInfo)")
if let screenName = screenName {
print("Navigate to Screen:\(screenName)")
}
if let actionKVPairs = kvPairs {
print("Selected Action KVPair:\(actionKVPairs)")
}
}
}
```
```objective-c Objective C wrap theme={null}
@interface AppDelegate ()
@end
@implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Set the delegate
[[MoEngageSDKMessaging sharedInstance] setMessagingDelegate:self forAppID:@"YOUR_WORKSPACE_ID"];
//Rest of the implementation
}
// Notification Clicked Callback
-(void)notificationClickedWithScreenName:(NSString *)screenName andKVPairs:(NSDictionary *)kvPairs{
if (screenName) {
NSLog(@"Screen Name : %@",screenName);
}
if (kvPairs) {
NSLog(@"KV Pairs : %@",kvPairs);
}
}
// Notification Clicked Callback with Push Payload
-(void)notificationClickedWithScreenName:(NSString *)screenName KVPairs:(NSDictionary *)kvPairs andPushPayload:(NSDictionary *)userInfo{
NSLog(@"Push Payload: %@",userInfo);
if (screenName) {
NSLog(@"Screen Name : %@",screenName);
}
if (kvPairs) {
NSLog(@"KV Pairs : %@",kvPairs);
}
}
@end
```
# Real-Time Triggers
Source: https://moengage.com/docs/developer-guide/ios-sdk/push/optional/real-time-triggers
Set up device-triggered push notifications that fire instantly when a user performs an event on iOS.
Real-time device triggers are push notifications that are triggered instantly in the device whenever a trigger event(configured while creating the campaign) is tracked with the SDK [trackEvent:](https://www.moengage.com/docs/developer-guide/ios-sdk/data-tracking/basic/tracking-events) method. In this case, the notifications are triggered in the device, which enables you to post notifications even in offline scenarios.
Real-Time Triggers are available from SDK version [4.0.0](/docs/release-notes/sdks/ios)
# SDK Installation
## Install using Swift Package Manager (Recommended)
MoEngageRealTimeTrigger is supported through SPM from SDK version 1.2.0. To integrate, use the GitHub url [https://github.com/moengage/apple-sdk.git](https://github.com/moengage/apple-sdk.git) for SDK versions equal and above 9.23.0, or [https://github.com/moengage/MoEngage-iOS-SDK.git](https://github.com/moengage/MoEngage-iOS-SDK.git) for other SDK versions link and set the branch as master or required version.
## Install using CocoaPod
**Information**
CocoaPods is being deprecated. MoEngage recommends using Swift Package Manager for all new integrations. For more info on CocoaPods, refer to [CocoaPods Integration Guide](https://www.moengage.com/docs/developer-guide/ios-sdk/sdk-integration/basic/integration-through-cocoa-pods).
From MoEngage-iOS-SDK version 8.2.0, MoEngageRealTimeTrigger module is separated from the SDK to a separate module as MoEngageRealTimeTrigger and hence has to be added separately.
Integrate the RealTimeTrigger framework by adding the dependency in the podfile as shown below.
```ruby Ruby wrap theme={null}
pod 'MoEngage-iOS-SDK/RealTimeTrigger',
```
Now run `pod install` to install the framework
# Manual Syncing
MoEngage SDK syncs all the real-time trigger campaigns whenever the app is **launched OR comes to the foreground**. But in case its needed to manually sync the device triggers for any of the background tasks, use [*syncRealTimeTriggers(forAppID:andCompletionHandler:)*](https://moengage.github.io/ios-api-reference/Classes/MoEngageSDKRealTimeTrigger.html#/c:@M@MoEngageRealTimeTrigger@objc\(cs\)MoEngageSDKRealTimeTrigger\(im\)syncRealTimeTriggersForAppID:andCompletionHandler:) as shown below:
```swift Swift wrap theme={null}
MoEngageSDKRealTimeTrigger.sharedInstance.syncRealTimeTriggers(forAppID: "YOUR_WORKSPACE_ID") { rtSyncCompleted in
if(rtSyncCompleted){
print("Real-Time trigger sync successfull")
}
}
```
```objective-c Objective C wrap theme={null}
[[MoEngageSDKRealTimeTrigger sharedInstance] syncRealTimeTriggersForAppID:@"YOUR_WORKSPACE_ID" andCompletionHandler:^(BOOL rtSyncCompleted) {
if (rtSyncCompleted) {
NSLog(@"Real-Time trigger sync successfull");
}
}];
```
# Additional Callbacks
**Note**
For iOS 10 and above, MoEngage SDK uses the UserNotification framework for triggering notifications and relies on the callbacks from **UNUserNotificationCenter** to obtain and process the notification payload. Therefore, make sure the SDK methods are called in UNUserNotificationCenter delegate callbacks as mentioned in this [doc](https://www.moengage.com/docs/developer-guide/ios-sdk/push/basic/ios-push-integration-tutorial#step-1-implement-notification-service-extension-nse).
# Transactional Live Activity
Source: https://moengage.com/docs/developer-guide/ios-sdk/push/optional/transactional-live-activity
Display real-time transactional updates on the iPhone Lock Screen using MoEngage Live Activities.
# Overview
[iOS Live Activities](https://developer.apple.com/design/human-interface-guidelines/live-activities) display your app's most current data as real-time, interactive updates on the iPhone Lock Screen and in the [Dynamic Island](https://support.apple.com/en-in/guide/iphone/iph28f50d10d/ios). Transactional Live Activities are specifically designed for unique, user-specific events such as order tracking, ride-hailing updates, or personalized transaction states.
**Information**
Live Activities and push notifications have different user permission models. By default, Live Activities are enabled for an app. Users can manage permissions for each app individually in their device settings.
**Prerequisites**
Before you begin, ensure your project and accounts are configured correctly.
1. **Apple Developer Account Configuration**:
* In your Apple Developer account, navigate to **Certificates**, **IDs & Profiles** > **Identifiers** and select your app's identifier.
* Under the **Capabilities** tab, ensure that **Push Notifications** checkbox is selected. This is mandatory for the Apple Push Notification service (APNs) to deliver activity updates.
* **APNs Authentication Key**: To authorize MoEngage to send push notifications on your behalf, you must configure an APNs Authentication Key. For detailed steps on how to upload the .p8 file to the MoEngage dashboard, please refer to the [documentation](/docs/developer-guide/ios-sdk/push/basic/apns-authentication-key) on APNs Authentication Key.
2. **SDK version:** MoEngage iOS SDK 10.09.0 or higher is required to support Transactional Live Activities.
3. **MoEngage Live Activity Module**: The MoEngageLiveActivity module is required to handle transactional updates.
4. **Xcode and iOS Version**:
* **Xcode**: Use Xcode 14.1 or later.
* **iOS Target**: Your Live Activity must target iOS 18 and later.
# Implementing a Transactional Live Activity
This section covers the client-side setup required within your Xcode project for Transactional Live Activities.
## Step 1: Add a Widget Extension
1. In Xcode, navigate to **File** > **New** > **Target**.
2. Select **Widget Extension** and click **Next**.
3. Enter a product name for your widget.
4. Select the **Include Live Activities** checkbox.
5. Click **Finish**.
## Step 2: Configure App's Info.plist
Add Live Activities support to your main app's Info.plist.
```xml XML wrap theme={null}
NSSupportsLiveActivities
```
## Step 3: MoEngageLiveActivity integration
**Information**
To integrate the MoEngageLiveActivity framework, ensure you are using MoEngage iOS SDK version 10.09.0 or higher.
#### **Install using Swift Package Manager (Recommended )**
The MoEngageLiveActivity framework is supported via SPM from SDK version 10.09.0. To integrate, use the GitHub URL [https://github.com/moengage/apple-sdk.git](https://github.com/moengage/apple-sdk.git) and set the branch as master or the required version.
#### **Install using CocoaPod**
**Information**
CocoaPods is being deprecated. MoEngage recommends using Swift Package Manager for all new integrations. For more info on CocoaPods, refer to [CocoaPods Integration Guide](/docs/developer-guide/ios-sdk/sdk-integration/basic/integration-through-cocoa-pods).
To integrate the MoEngageLiveActivity framework, add the following dependency to your Podfile:
```ruby Ruby wrap theme={null}
target 'MoETest' do
use_frameworks!
# Pods for app target
pod 'MoEngage-iOS-SDK' # specify version constraint
pod 'MoEngage-iOS-SDK/LiveActivity'
target 'LiveActivity' do
use_frameworks!
inherit! :search_paths
# Pods for live activity extension target
pod 'MoEngage-iOS-SDK/LiveActivity'
end
end
```
## Step 4: Define the Live Activity Attributes
In the Swift file generated for your widget extension, define the data structure for your Transactional Live Activity (e.g., a Food Delivery order).
1. Configure ActivityAttributes: Create a struct that conforms to [ActivityAttributes](https://developer.apple.com/documentation/activitykit/activityattributes). This struct will contain:
* **Static Data**: Attributes that are set once and do not change (e.g., Order Number).
* **ContentState**: A nested struct for dynamic data that will be updated in real-time (e.g., Delivery Status).
```swift Swift wrap theme={null}
import Foundation
import ActivityKit
import WidgetKit
import SwiftUI
struct FoodOrderAttributes: ActivityAttributes {
public struct ContentState: Codable, Hashable {
// Dynamic stateful properties about your order go here!
var status: String // e.g., "Out for Delivery"
var estimatedMinutes: Int
}
// Fixed non-changing properties about your order go here!
var orderNumber: String
var restaurantName: String
}
```
2. When creating ActivityConfiguration, use *MoEngageTransactionActivityAttributes\* instead of FoodOrderAttributes as your ActivityAttributes type for transactional support.
3. Track widget clicks by configuring the deeplink and widget ID with the moengageWidgetClickURL API.
```swift Swift wrap theme={null}
import ActivityKit
import WidgetKit
import SwiftUI
import MoEngageLiveActivity
struct FoodOrderWidget: Widget {
var body: some WidgetConfiguration {
ActivityConfiguration(for: MoEngageTransactionActivityAttributes.self) { context in
// Lock screen/banner UI goes here
VStack(spacing: 12) {
Text(context.attributes.appAttributes.restaurantName)
Text(context.state.appContent.status)
}
.moengageWidgetClickURL(URL(string: "moeapp://order-tracking"), context: context, widgetId: 2)
} dynamicIsland: { context in
DynamicIsland {
// Expanded UI goes here
} compactLeading: {
Text("Preparing")
} compactTrailing: {
Text("\(context.state.appContent.estimatedMinutes)m")
} minimal: {
Text("Preparing")
}
.moengageWidgetClickURL(URL(string: "moeapp://order-tracking"), context: context, widgetId: 1)
}
}
}
```
4. **Ensure Target Membership**: Make your ActivityAttributes struct accessible to your main app target.
1. Select the Swift file where you defined your ActivityAttributes.
2. Open the **File Inspector** (Option + Command + 1).
3. In the **Target Membership** section, check the box for your main app target.
## Step 5: Monitor Live Activities
Call the `monitorLiveActivities` method to register the `ActivityAttributes` types that the SDK should monitor for real-time updates. **`This method must be invoked within your app's didFinishLaunchingWithOptions method.`** This registration is mandatory for the SDK to successfully track and manage the short tokens required for transactional activity updates.
```swift Swift wrap theme={null}
Task {
if #available(iOS 18, *) {
await MoEngageSDKLiveActivity.monitorLiveActivities(types: [ FoodOrderAttributes.self]) { data in
print("Push token generated for live activity",data)
}
}
}
public struct MoEngageTransactionCampaignData {
public struct MoEngageTokenData {
public let transactionId: String
public let shortToken: String
}
public let accountMeta: MoEngageAccountMeta
public let tokenData: MoEngageTokenData
}
public class MoEngageAccountMeta {
/// Account identifier, APP ID on the MoEngage Dashboard.
public let appID: String
}
```
# Managing the Live Activity Lifecycle
Once your app is configured, you can start, update, and end Transactional Live Activities using a combination of local app code and MoEngage APIs.
## Step 6: Start a Transactional Live Activity
**Live Activity tracking validations**
* **Track before initialization.** Live Activity tracking methods must be called only once the SDK is initialized. Calling them earlier throws a fatal exception and crashes the app in `DEBUG` builds. In Release and TestFlight builds, the call is dropped silently and logged.
* **Duplicate `trackStarted`.** Calling `MoEngageSDKLiveActivity.trackStarted` more than once for the same Live Activity throws a fatal exception in `DEBUG` builds. In Release and TestFlight builds, the duplicate call is ignored.
* If you are upgrading an existing app and these strict `DEBUG` validations cause disruptive crashes while you refactor your tracking code, you can temporarily opt out by calling `disableIntegrationValidator()` during SDK initialization. Use this strictly as a stopgap for phased upgrades, and aim to remove the opt-out once your attribute call sites are properly validated.
A Transactional Live Activity is typically started locally from the app when an end user initiates a transaction (e.g., placing a food order) or you can initiate it remotely via the Inform API (Server-side).
### Push-to-Start (Remote)
Start a live activity for a user using the Inform API. For more information, refer [here](https://www.moengage.com/docs/api/live-activities/start-broadcast-live-activity).
### Click-to-Start (Local)
Start an activity from within the app, triggered by a user action.
Get Live Activity data from the [createAttributes(withCampaign:completion:)](https://moengage.github.io/ios-api-reference/Enums/MoEngageSDKLiveActivity.html#/s:20MoEngageLiveActivity0ab7SDKLiveD0O16createAttributes12withCampaign4file0J2Id6method4line6column10completionyAC0I0Vy_xG_s12StaticStringVA2PS2uyAM6ResultVy_x_GSgScMYcct0D3Kit0dG0RzlFZ) or [createAttributes(withCampaign:) async](https://moengage.github.io/ios-api-reference/Enums/MoEngageSDKLiveActivity.html#/s:20MoEngageLiveActivity0ab7SDKLiveD0O16createAttributes12withCampaign4file0J2Id6method4line6columnAC0I0V6ResultVy_x_GSgALy_xG_s12StaticStringVA2SS2utYa0D3Kit0dG0RzlFZ) SDK APIs by combining your application's ActivityAttributes data with mandatory MoEngage metadata (retrieved from your server). Use Apple's Activity.request() method with pushType as `.token` to start Live Activity. This links the locally started activity to your campaign.
```swift Swift wrap theme={null}
MoEngageSDKLiveActivity.createAttributes(
withCampaign: .init(
campaignId: campaignId,
campaignName: "Order Tracking \(orderId)",
transactionId: transactionId,
attributeType: "\(FoodOrderAttributes.self)",
instanceId: instanceId,
appAttributes: FoodOrderAttributes(orderNumber: "ORD-123", restaurantName: "Pizza Palace"),
appContent: FoodOrderAttributes.ContentState(status: "Preparing", estimatedMinutes: 30)
)
) { [weak self] result in
guard let result = result else {
self?.view.makeToast("No Live Activity creation result")
return
}
do {
let activity = try MoEngageTransactionActivity.request(
attributes: result.attributes,
content: .init(
state: result.content,
staleDate: .distantFuture,
relevanceScore: 10
),
pushType: .token,
style: .standard
)
MoEngageSDKLiveActivity.trackStarted(activity: activity)
self?.view.makeToast("Order Tracking Live Activity started successfully")
} catch {
self?.view.makeToast("Activity request error: \"\(error.localizedDescription)\"")
}
}
```
**Information**
By using `pushType: .token,`the Live Activity is configured to receive transactional updates specifically targeted to this activity instance via its unique push token.
## Step 7: Update a Live Activity
Update the transactional activity status using a push notification targeted at the activity's push token. Updates must be performed exclusively via the Inform API. For more information, refer to the [Inform API](https://www.moengage.com/docs/api/transactional-alerts/send-transactional-alert).
## Step 8: End a Live Activity
Ending an activity must be performed exclusively through the Inform API; otherwise, it will terminate automatically upon reaching the configured `dismissal_date`.
For more information, refer to the [Inform API](https://www.moengage.com/docs/api/transactional-alerts/send-transactional-alert).
# iOS Sample App
Source: https://moengage.com/docs/developer-guide/ios-sdk/sample-app/i-os-sample-app
Explore the MoEngage iOS sample application on GitHub as a reference for your SDK integration.
**Sample App**
The [MoEngage iOS Sample application](https://github.com/moengage/iOS-SampleApp) offers a useful reference point for integrating MoEngage into your iOS app.
## Next Steps
* [SDK Installation](/docs/developer-guide/ios-sdk/sdk-integration/basic/sdk-integration)
* [Release Checklist](/docs/developer-guide/ios-sdk/checklist/release-checklist)
# Add-On Security
Source: https://moengage.com/docs/developer-guide/ios-sdk/sdk-integration/advanced/add-on-security
Encrypt data stored by the MoEngage iOS SDK on device using Keychain and encrypted storage.
# Encrypted Storage
By default, all the data stored by the SDK on the device is inside the application sandbox. This prevents other applications from accessing the data(both read and write). Due to compliance standards or any other use cases, you might want additionally encrypt the data stored on the SDK.
## Keychain Set Up
To ensure the encryption works as expected, follow the below steps to set up the Keychain Sharing.
1. Turn on Keychain sharing in Xcode with the below steps:
a. Select your app target and click the ***Signing & Capabilities*** tab.
b. Turn on the ***Keychain Sharing*** capability.
2. Specify the Keychain group name
3. App ID Prefix and Keychain group name: Xcode automatically prefixes keychain groups with your team ID. This ensures that your groups are specific to your development team. In order to see how it works, click on the *.entitlements* file and look at the value of the *Keychain Access Groups* array.
4. Get your AppID: The App ID Prefix (also called Team ID) is a unique text identifier associated with your Apple developer account that allows the sharing of keychain and pasteboard items between your apps.
Assume the AppID is ***AB123CDE45***, Keychain group name is ***AB123CDE45.com.example.sharedItems***. Make sure to pass the same keychain group name to MoEngage SDK via the [***keyChainConfig***](https://moengage.github.io/ios-api-reference/Classes/MoEngageSDKConfig.html#/c:@M@MoEngageCore@objc\(cs\)MoEngageSDKConfig\(py\)keyChainConfig) property on [***MoEngageSDKConfig***](https://moengage.github.io/ios-api-reference/Classes/MoEngageSDKConfig.html) object.
## Enabling Encryption
You can enable the storage encryption by setting the [***storageConfig***](https://moengage.github.io/ios-api-reference/Classes/MoEngageSDKConfig.html#/c:@M@MoEngageCore@objc\(cs\)MoEngageSDKConfig\(py\)storageConfig) property on the [***MoEngageSDKConfig***](https://moengage.github.io/ios-api-reference/Classes/MoEngageSDKConfig.html) while initializing the SDK.
```swift Swift wrap theme={null}
let sdkConfig = MoEngageSDKConfig(appId: "YOUR_WORKSPACE_ID", dataCenter: .data_center_01)
sdkConfig.storageConfig = MoEngageStorageConfig(encryptionConfig: MoEngageStorageEncryptionConfig(isEncryptionEnabled: true))
sdkConfig.keyChainConfig = MoEngageKeyChainConfig(groupName: "YOUR_KEYCHAIN_GROUP_NAME")
```
```objective-c Objective C wrap theme={null}
MoEngageSDKConfig* sdkConfig = [[MoEngageSDKConfig alloc] initWithAppId:@"YOUR_WORKSPACE_ID" dataCenter:MoEngageDataCenterData_center_01];
sdkConfig.storageConfig = [[MoEngageStorageConfig alloc] initWithEncryptionConfig: [[MoEngageStorageEncryptionConfig alloc] initWithIsEncryptionEnabled:true]];
sdkConfig.keyChainConfig = [[MoEngageKeyChainConfig alloc] initWithGroupName:@"YOUR_KEYCHAIN_GROUP_NAME"];
```
**Note**
Once storage encryption is enabled and a build is released to production(App Store), you should not disable encryption. Disabling the encryption after the build is released will result in a new user being created in the MoEngage system when the user updates the application.
* When storage encryption is enabled, you must pass a valid keychain group to `MoEngageKeyChainConfig`. If the keychain group is missing or your app is not configured with the matching Keychain Sharing capability, the SDK throws a fatal exception and crashes the app in `DEBUG` builds. In release builds, storage encryption fails to initialize and the SDK falls back to unencrypted storage.
* If you are upgrading an existing app and these strict `DEBUG` validations cause disruptive crashes while you refactor your tracking code, you can temporarily opt out by calling `disableIntegrationValidator()` during SDK initialization. Use this strictly as a stopgap for phased upgrades, and aim to remove the opt-out once your attribute call sites are properly validated.
# Encrypted Network Communication
By default, we use HTTPS protocol for all requests made from the SDK; HTTPS encrypts the requests by default. MoEngage SDK optionally adds another layer of encryption apart from the encryption done by HTTPS.
## Enabling Encryption
You can enable the storage encryption setting in the [***networkConfig***](https://moengage.github.io/ios-api-reference/Classes/MoEngageSDKConfig.html#/c:@M@MoEngageCore@objc\(cs\)MoEngageSDKConfig\(py\)networkConfig) property on the [***MoEngageSDKConfig***](https://moengage.github.io/ios-api-reference/Classes/MoEngageSDKConfig.html) while initializing the SDK.
```swift Swift wrap theme={null}
let sdkConfig = MoEngageSDKConfig(appId: appId, dataCenter: .data_center_01)
sdkConfig.networkConfig = MoEngageNetworkRequestConfig(dataSecurityConfig: MoEngageNetworkDataSecurityConfig(isEncryptionEnabled: true, encryptionKeyDebug: "YOUR_TEST_ENVIRONMENT_ENCRYPTION_KEY", encryptionKeyRelease: "YOUR_LIVE_ENVIRONMENT_ENCRYPTION_KEY"))
```
```objective-c Objective C wrap theme={null}
MoEngageSDKConfig* sdkConfig = [[MoEngageSDKConfig alloc] initWithAppId:@"YOUR_WORKSPACE_ID" dataCenter:MoEngageDataCenterData_center_01];
sdkConfig.networkConfig = [[MoEngageNetworkRequestConfig alloc] initWithDataSecurityConfig:[[MoEngageNetworkDataSecurityConfig alloc] initWithIsEncryptionEnabled:true encryptionKeyDebug:@"YOUR_TEST_ENVIRONMENT_ENCRYPTION_KEY" encryptionKeyRelease:@"YOUR_LIVE_ENVIRONMENT_ENCRYPTION_KEY"]];;
```
**Note**
1. When using encrypted network communication, we strongly recommend you enable Storage encryption as well.
2. Adding the above dependency and enabling the flag isn't enough for this feature to work; there is some additional configuration required on our side to enable this feature completely. In case you want to use this feature, reach out to your account manager or the MoEngage Support team.
When network encryption is enabled (`isEncryptionEnabled: true`), you must provide non-empty values for both `encryptionKeyDebug` and `encryptionKeyRelease`. Passing an empty key for the active build configuration throws a fatal exception and crashes the app in `DEBUG` builds. In release builds, network requests fall back to standard HTTPS without the additional encryption layer.
# Custom Proxy Domain - iOS
Source: https://moengage.com/docs/developer-guide/ios-sdk/sdk-integration/advanced/custom-proxy-domain-ios
Route MoEngage SDK traffic through your own subdomain to bypass ad blockers on iOS.
In today's privacy-focused digital landscape, many users employ ad blockers or private DNS services. These tools often block network requests to known third-party domains, including analytics and engagement platforms. When these requests are blocked, you lose critical data visibility, and your users may not receive in-app messages or push notifications.
To ensure reliable campaign delivery and campaign performance, MoEngage offers the **Custom Proxy Domain** feature. This allows you to route MoEngage SDK traffic through a subdomain of your own primary domain (e.g., `data.yourcompany.com`). Because the traffic appears as first-party communication, it bypasses common ad-blocking lists.
## Onboarding Process
Setting up a Custom Proxy Domain requires a one-time DNS delegation process between your team and MoEngage.
**Prerequisite**
Before updating your SDK code, you must complete the DNS delegation setup. For a detailed guide on picking a domain and configuring NS records, refer to [DNS Delegation](https://www.moengage.com/docs/user-guide/getting-started/integration/custom-proxy-sub-domains).
### Step 1: Choose a Subdomain
Select a subdomain that is short and does not contain keywords typically flagged by filters (e.g., avoid "tracking", "ads", or "moengage").
### Step 2: Request DNS Delegation
Contact your MoEngage Customer Success Manager (CSM) or Support Team to initiate the request. Provide your chosen subdomain.
### Step 3: Configure NS Records
MoEngage will provide you with a list of Name Server (NS) records. You must add these records to your DNS provider's configuration for the chosen subdomain.
## Implementation
Once the DNS delegation is verified, update your SDK initialization logic. The SDK will dynamically rewrite all MoEngage endpoints (API calls and CDN assets) to use your custom proxy domain.
### Update MoEngage Configuration
```swift Swift wrap theme={null}
import MoEngageSDK
let sdkConfig = MoEngageSDKConfig(appId: "YOUR_WORKSPACE_ID", dataCenter: DATA_CENTER)
sdkConfig.customBaseDomain = "CUSTOM_DOMAIN"
#if DEBUG
MoEngage.sharedInstance.initializeDefaultTestInstance(sdkConfig)
#else
MoEngage.sharedInstance.initializeDefaultLiveInstance(sdkConfig)
#endif
```
```objective-c Objective C wrap theme={null}
@import MoEngageSDK;
MoEngageSDKConfig* sdkConfig = [[MoEngageSDKConfig alloc] initWithAppId:@"YOUR_WORKSPACE_ID" dataCenter: DATA_CENTER];
sdkConfig.customBaseDomain = @"CUSTOM_DOMAIN";
#ifdef DEBUG
[[MoEngage sharedInstance] initializeDefaultTestInstance:sdkConfig];
#else
[[MoEngage sharedInstance] initializeDefaultLiveInstance:sdkConfig];
#endif
```
## Best Practices and Validation
1. **Domain Selection:** Keep your subdomain string short (5-8 characters).
2. **Network Logs:** Verify that requests start with your custom subdomain (e.g., `sdk-01.data.example.com`).
3. **Asset Loading:** Ensure images in campaigns load correctly.
# JWT Authentication
Source: https://moengage.com/docs/developer-guide/ios-sdk/sdk-integration/advanced/jwt-authentication
Secure your MoEngage data collection by implementing JWT authentication in your iOS application.
## Overview
JWT (JSON Web Token) authentication is a standard method for securely verifying user identity. By implementing JWT authentication, you add a critical layer of security to your data collection process with MoEngage.
The feature ensures that the data sent on behalf of your identified users is authentic and has not been tampered with. This security is achieved by requiring a token that is cryptographically signed by your own server, which prevents unauthorized users from impersonating your legitimate users.
**Prerequisites**
Before you begin the implementation, please ensure you meet the following requirements:
* Your application must use the MoEngage iOS SDK version ***10.08.0*** or higher to access the JWT authentication feature.
* You must have access to your MoEngage dashboard to manage public keys and configure the feature's enforcement settings.
The following diagram illustrates the interaction between your application, your server, the MoEngage SDK, and the MoEngage server:
## Integration
Follow these steps to integrate JWT authentication into your iOS application.
### Step 1: Enable JWT Authentication
You can enable JWT authentication during [SDK initialization](/docs/developer-guide/ios-sdk/sdk-integration/basic/sdk-initialization) by configuring the [***networkConfig***](https://moengage.github.io/ios-api-reference/Classes/MoEngageSDKConfig.html#/c:@M@MoEngageCore@objc\(cs\)MoEngageSDKConfig\(py\)networkConfig).[***authorizationConfig***](https://moengage.github.io/ios-api-reference/Classes/MoEngageNetworkRequestConfig.html#/c:@M@MoEngageCore@objc\(cs\)MoEngageNetworkRequestConfig\(py\)authorizationConfig) property on the [***MoEngageSDKConfig***](https://moengage.github.io/ios-api-reference/Classes/MoEngageSDKConfig.html) object.
```swift Swift wrap theme={null}
let sdkConfig = MoEngageSDKConfig(appId: "", dataCenter: .YOUR_DATA_CENTER)
sdkConfig.networkConfig = MoEngageNetworkRequestConfig(authorizationConfig: MoEngageNetworkAuthorizationConfig(isJwtEnabled: true))
#if DEBUG
MoEngage.sharedInstance.initializeDefaultTestInstance(sdkConfig)
#else
MoEngage.sharedInstance.initializeDefaultLiveInstance(sdkConfig)
#endif
```
```objective-c Objective C wrap theme={null}
MoEngageSDKConfig* sdkConfig = [[MoEngageSDKConfig alloc] initWithAppId:@"" dataCenter:YOUR_DATA_CENTER];
sdkConfig.networkConfig = [[MoEngageNetworkRequestConfig alloc] initWithAuthorizationConfig:[[MoEngageNetworkAuthorizationConfig alloc] initWithIsJwtEnabled:YES]];
#ifdef DEBUG
[[MoEngage sharedInstance] initializeDefaultTestInstance:sdkConfig];
#else
[[MoEngage sharedInstance] initializeDefaultLiveInstance:sdkConfig];
#endif
```
### Step 2: Pass the JWT to the SDK
Your application is responsible for managing the JWT lifecycle. The recommended flow is to fetch a token upon user login and pass the token to the SDK. You should also check if the token has expired on subsequent app launches and fetch a new one if necessary.
Use the [***MoEngageSDKCore.passAuthenticationDetails()***](https://moengage.github.io/ios-api-reference/Classes/MoEngageSDKCore.html#/c:@CM@MoEngageCore@objc\(cs\)MoEngageSDKCore\(im\)passAuthenticationDetails:workspaceId:) method to provide the token to the SDK.
* `MoEngageSDKCore.sharedInstance.passAuthenticationDetails` requires JWT to be enabled in `MoEngageNetworkAuthorizationConfig` during SDK initialization (Step 1 above). Calling this method without first enabling JWT throws a fatal exception and crashes the app in `DEBUG` builds. In release builds, the call is dropped silently and the token is not registered with the SDK.
* If you are upgrading an existing app and these strict `DEBUG` validations cause disruptive crashes while you refactor your tracking code, you can temporarily opt out by calling `disableIntegrationValidator()` during SDK initialization. Use this strictly as a stopgap for phased upgrades, and aim to remove the opt-out once your attribute call sites are properly validated.
In the iOS SDK, `DEBUG` mode means the SDK was initialized using `initializeDefaultTestInstance(_:)` (TEST workspace) and the app is running attached to Xcode. This is different from a release build that has debug symbols enabled.
```swift Swift wrap theme={null}
let jwtDetails = MoEngageJwtAuthenticationDetails(token: "your_jwt_token", identifier: "user_id")
MoEngageSDKCore.sharedInstance.passAuthenticationDetails(jwtDetails)
```
```objective-c Objective C wrap theme={null}
MoEngageJwtAuthenticationDetails* jwtDetails = [[MoEngageJwtAuthenticationDetails alloc] initWithToken:@"your_jwt_token" identifier:@"user_id"];
[[MoEngageSDKCore sharedInstance] passAuthenticationDetails:jwtDetails];
```
### Step 3: Handle Authentication Errors
To handle token validation errors that the MoEngage server returns, you must register an error listener using [***MoEngageSDKCore.registerAuthenticationListener()***](https://moengage.github.io/ios-api-reference/Classes/MoEngageSDKCore.html#/c:@CM@MoEngageCore@objc\(cs\)MoEngageSDKCore\(im\)registerAuthenticationListener:workspaceId:) method. The SDK invokes this listener when an authentication error occurs, which allows your application to fetch and provide a new token.
Starting with iOS SDK [11.0.0](/docs/release-notes/sdks/ios#22nd-july-2026), `MoEngageAuthenticationError.Listener` adopts `Sendable` and `@MainActor` isolation, and the SDK invokes `onError(_:)` on the main actor. Conforming types must remain safe to use across concurrency domains. The example above remains unaffected because it does not capture non-`Sendable` state.
```swift Swift wrap theme={null}
class JwtAuthenticationListener: NSObject, MoEngageAuthenticationError.Listener {
func onError(_ error: MoEngageAuthenticationError) {
print("Authentication Error Received:")
if let jwtError = error as? MoEngageJwtAuthenticationError {
print("- Code: \(jwtError.details.code.rawValue) - \(jwtError.details.code.description)")
print("- Token: \(jwtError.details.token == nil ? "No token" : String(jwtError.details.token!.prefix(20)) + "...")")
print("- Identifier: \(jwtError.details.identifier == nil ? "No identifier" : jwtError.details.identifier!)")
print("- Message: \(jwtError.details.message ?? "No message")")
} else {
print("- Message: \(error.details.message ?? "No message")")
}
print("- Account: \(error.accountMeta.appID)")
}
}
// Register listner after SDK initialization
let listener = JwtAuthenticationListener()
MoEngageSDKCore.sharedInstance.registerAuthenticationListener(listener)
```
```objective-c Objective C wrap theme={null}
@interface JwtAuthenticationListener : NSObject
@end
@implementation JwtAuthenticationListener
- (void)onError:(MoEngageAuthenticationError *)error {
NSLog(@"Authentication Error Received:");
if ([error isKindOfClass:[MoEngageJwtAuthenticationError class]]) {
MoEngageJwtAuthenticationError *jwtError = (MoEngageJwtAuthenticationError *)error;
NSLog(@"- Code: %ld - %@", (long)jwtError.details.code.rawValue, jwtError.details.code.description);
NSString *tokenDisplay = jwtError.details.token ?
[[jwtError.details.token substringToIndex:MIN(20, jwtError.details.token.length)] stringByAppendingString:@"..."] :
@"No token";
NSLog(@"- Token: %@", tokenDisplay);
NSLog(@"- Identifier: %@", jwtError.details.identifier ?: @"No identifier");
NSLog(@"- Message: %@", jwtError.details.message ?: @"No message");
} else {
NSLog(@"- Message: %@", error.details.message ?: @"No message");
}
NSLog(@"- Account: %@", error.accountMeta.appID);
}
@end
//
JwtAuthenticationListener *listener = [[JwtAuthenticationListener alloc] init];
[[MoEngageSDKCore sharedInstance] registerAuthenticationListener:listener];
```
### Step 4: Register the listener after SDK initialization
Register the [***MoEngageAuthenticationError.Listener***](https://moengage.github.io/ios-api-reference/Classes/MoEngageAuthenticationError/Listener.html) in a global scope, such as the ***applicaton(\_:didFinishLaunchingWithOptions:)*** method of your AppDelegate class, to ensure your application always receives callbacks.
```swift Swift wrap theme={null}
let listener = JwtAuthenticationListener()
MoEngageSDKCore.sharedInstance.registerAuthenticationListener(listener)
```
```objective-c Objective C wrap theme={null}
JwtAuthenticationListener *listener = [[JwtAuthenticationListener alloc] init];
[[MoEngageSDKCore sharedInstance] registerAuthenticationListener:listener];
```
**Information**
* If an API request fails due to an authentication error, the SDK will not retry the request until your application provides a new token.
* After 10 consecutive authentication failures in a single session, the SDK will stop attempting to sync data until the next session begins. This counter resets after any successful sync.
* Upon user logout, if a data sync fails due to a JWT error, the pending data will be deleted, and no retry will be attempted.
# Configuring Project for Architecture Compatibility
Source: https://moengage.com/docs/developer-guide/ios-sdk/sdk-integration/basic/Configuring-Project-for-Architecture-Compatibility
Configure your Xcode project for arm64 architecture compatibility with MoEngage iOS SDK v10.x.x.
## Overview
**Note**
The changes mentioned in this article are also applied to the CI/CD environment.
Beginning with MoEngage iOS SDK v10.x.x, support for the `x86_64` and `x86` architectures are discontinued. The SDK only supports the `arm64` architecture.
Attempting to build a project for a simulator that runs on x86\_64 architecture used by the [Rosetta](https://developer.apple.com/documentation/apple-silicon/about-the-rosetta-translation-environment) simulator, or a simulator running on an Intel-based Mac (x86\_64), may result in linker errors. This article outlines the procedures for configuring an Xcode project to resolve these errors.
## Identify Host Machine Architecture
To determine the correct configuration procedure, identify the development machine's architecture by executing the following command in the terminal:
```shellscript Shell theme={null}
uname -m
```
The command returns one of the following outputs:
* **`arm64`**: An Apple silicon Mac.
* **`x86_64`**: An Intel-based Mac.
## Configuration Procedures
### For Apple silicon Macs (`arm64`)
On Apple silicon Macs, linker errors occur if the build targets a [Rosetta](https://developer.apple.com/documentation/apple-silicon/about-the-rosetta-translation-environment) simulator, which runs on the `x86_64` architecture. To prevent this, configure the build to run only on the native simulator (`arm64` architecture) as shown below:
1. In Xcode, select your project in the **Project Navigator**, and perform the steps below for all your targets (including Pods targets).
2. Navigate to the **Build Settings** tab.
3. Apply the following configurations for the `Debug` build:
* **`Excluded Architectures (EXCLUDED_ARCHS)`**
* Ensure this setting does not contain `arm64` for simulator builds.
* **`Build Active Architecture Only (ONLY_ACTIVE_ARCH)`**
* Set this value to **Yes**. This setting directs Xcode to build only for the architecture of the currently selected simulator.
* **Architectures** set to ARCHS\_STANDARD or include arm64.
### For Intel-based Macs (`x86_64`)
On Intel-based Macs, the iOS Simulator runs on the `x86_64` architecture. As MoEngage SDK v10.x.x and later do not support this architecture, projects cannot run on the simulator. To build and test the application, use a physical iOS device.
# Data Center
Source: https://moengage.com/docs/developer-guide/ios-sdk/sdk-integration/basic/data-center
Configure data center redirection in the MoEngage iOS SDK to route data to the correct cluster.
We support data redirection to our servers in different clusters. Use [*MoEngageSDKConfig*](https://moengage.github.io/ios-api-reference/Classes/MoEngageSDKConfig.html) initializer to set the data center according to your account's configuration.
```swift Swift wrap theme={null}
import MoEngageSDK
let sdkConfig = MoEngageSDKConfig(appId: "YOUR_WORKSPACE_ID", dataCenter: .data_center_01)
// Possible values for dataCenter:
// .data_center_01, .data_center_02, .data_center_03,
// .data_center_04, .data_center_05, .data_center_06 (available from SDK version 9.17.3)
```
```objective-c Objective C wrap theme={null}
@import MoEngageSDK;
MoEngageSDKConfig* config = [[MoEngageSDKConfig alloc] initWithAppId:@"YOUR_WORKSPACE_ID" dataCenter:MoEngageDataCenterData_center_01];
// Possible Values for Data Center
typedef enum {
data_center_01,
data_center_02,
data_center_03,
data_center_04,
data_center_05,
data_center_06 /// Data center 06 available SDK version 9.17.3
}MoEngageDataCenter;
```
Following is the host for different data centers; please update the app's configuration according to the DataCenter in case would want to whitelist the SDK API domain:
| Data Center | SDK Host | Dashboard Host |
| :--------------- | :------------------ | :------------------------ |
| data\_center\_01 | sdk-01.moengage.com | dashboard-01.moengage.com |
| data\_center\_02 | sdk-02.moengage.com | dashboard-02.moengage.com |
| data\_center\_03 | sdk-03.moengage.com | dashboard-03.moengage.com |
| data\_center\_04 | sdk-04.moengage.com | dashboard-04.moengage.com |
| data\_center\_05 | sdk-05.moengage.com | dashboard-05.moengage.com |
| data\_center\_06 | sdk-06.moengage.com | dashboard-06.moengage.com |
**Important**
Refer to the dashboard host to know the Data Center of your account. Please make sure that you consult with the MoEngage team before using this method for changing the data center in the SDK.
For more information about MoEngage data centers, refer to [Data Centers in MoEngage](https://www.moengage.com/docs/user-guide/data/key-concepts/data-centers-in-moengage).
# Integration through CocoaPods
Source: https://moengage.com/docs/developer-guide/ios-sdk/sdk-integration/basic/integration-through-cocoa-pods
Install the MoEngage iOS SDK using CocoaPods dependency manager for your Xcode project.
**Information**
CocoaPods is being deprecated. MoEngage recommends using Swift Package Manager for all new integrations. For more information, refer [here](/docs/developer-guide/ios-sdk/sdk-integration/basic/sdk-integration).

Cocoapods is a dependency manager for Objective C & Swift projects and makes integration easier.
1. If you don't have CocoaPods installed, you can do it by executing the following line in your terminal.
```ruby Ruby theme={null}
sudo gem install cocoapods
```
2. If you don't have a Podfile, then create one by using `pod init` command. Post this add `MoEngage-iOS-SDK` pod to your pod file as shown below:
```ruby Ruby theme={null}
pod 'MoEngage-iOS-SDK',
```
**Information**
* To ensure automatic minor version updates for MoEngage-iOS-SDK, configure your Podfile with `pod 'MoEngage-iOS-SDK', '~> Major.Minor.Build'`. For example: `'MoEngage-iOS-SDK', '~>10.03.2`
* For automatic integration of the latest MoEngage-iOS-SDK version, including major changes, use `pod 'MoEngage-iOS-SDK'`.
3. Integrate MoEngage iOS SDK by executing the following in the terminal at your project's root directory:
```ruby Ruby theme={null}
pod repo update
pod install
```
4. Now, open your project workspace and check if MoEngage SDK is properly added.
# SDK Initialization
Source: https://moengage.com/docs/developer-guide/ios-sdk/sdk-integration/basic/sdk-initialization
Initialize the MoEngage iOS SDK in your AppDelegate using your Workspace ID and data center.
# Initializing MoEngage SDK
First, go to `Build Settings` of your `App Target` and ensure that **DEBUG** Preprocessor Macro is defined in `Debug` section as shown in the below image, if not present then add the same by entering `DEBUG=1` in `Debug` section:
**For Swift Project**, In `App Target` `Build Settings` make sure **-DDEBUG** is added to `Debug` section in the `Other Swift Flags` as described in the image:
* in to your MoEngage account, go to **Settings** in the left panel of the dashboard. Under General Settings, you will find your **Workspace ID**. Provide this Workspace ID along with the Datacenter while initializing the SDK with MoEngageSDKConfig instance . Use[***initializeDefaultTestInstance(\_:)***](https://moengage.github.io/ios-api-reference/Classes/MoEngage.html#/c:@M@MoEngageSDK@objc\(cs\)MoEngage\(im\)initializeDefaultTestInstance:) and [***initializeDefaultLiveInstance(\_:)***](https://moengage.github.io/ios-api-reference/Classes/MoEngage.html#/c:@M@MoEngageSDK@objc\(cs\)MoEngage\(im\)initializeDefaultLiveInstance:) methods as shown below.
- **Breaking change in iOS SDK [11.0.0](/docs/release-notes/sdks/ios#22nd-july-2026):** The SDK now enforces initialization before any public API use across all build configurations — Debug, Release, and TestFlight builds. Calling a public API before initializing the SDK triggers a `fatalError` rather than failing silently. Invoke `initializeDefaultTestInstance(_:)` or `initializeDefaultLiveInstance(_:)` before all other MoEngage API calls.
- Starting with iOS SDK [11.0.0](/docs/release-notes/sdks/ios#22nd-july-2026), `initializeDefaultLiveInstance(_:)` returns a typed task object instead of `Void`. Direct calls like the ones above compile unchanged. Chaining `.onSuccess { ... }` / `.onFailure { ... }` or calling the async `result()` method provides per-call visibility into success or failure.
```swift Swift wrap theme={null}
import MoEngageSDK
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
//Add your MoEngage Workspace ID and Data center.
let sdkConfig = MoEngageSDKConfig(appId: "YOUR_WORKSPACE_ID", dataCenter: DATA_CENTER)
// MoEngage SDK Initialization
// Separate initialization methods for Dev and Prod initializations
#if DEBUG
MoEngage.sharedInstance.initializeDefaultTestInstance(sdkConfig)
#else
MoEngage.sharedInstance.initializeDefaultLiveInstance(sdkConfig)
#endif
//Rest of the implementation of method
//-------
return true
}
```
```objective-c Objective C wrap theme={null}
@import MoEngageSDK;
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
//TODO: Add your MoEngage Workspace ID and Data center.
MoEngageSDKConfig* sdkConfig = [[MoEngageSDKConfig alloc] initWithAppId:@"YOUR_WORKSPACE_ID" dataCenter: DATA_CENTER];
// MoEngage SDK Initialization
// Separate initialization methods for Dev and Prod initializations
#ifdef DEBUG
[[MoEngage sharedInstance] initializeDefaultTestInstance:sdkConfig];
#else
[[MoEngage sharedInstance] initializeDefaultLiveInstance:sdkConfig];
#endif
//Rest of the implementation of method
//-------
}
```
* In your MoEngage account, if your [portfolio](https://help.moengage.com/hc/en-us/articles/40394603054100-Portfolio) is configured with multiple projects, Provide the respective Project ID along with the Datacenter while initializing the SDK with MoEngageSDKConfig instance . Use [initializeDefaultTestInstance(\_:)](https://moengage.github.io/ios-api-reference/Classes/MoEngage.html#/c:@M@MoEngageSDK@objc\(cs\)MoEngage\(im\)initializeDefaultTestInstance:) and[ initializeDefaultLiveInstance(\_:)](https://moengage.github.io/ios-api-reference/Classes/MoEngage.html#/c:@M@MoEngageSDK@objc\(cs\)MoEngage\(im\)initializeDefaultLiveInstance:) methods as shown below:
- **Portfolio attributes without `projectConfig`.** If you set any portfolio attribute without passing a non-empty `projectID` during SDK initialization, the SDK throws an exception, resulting in an application crash in `DEBUG` mode. If you are using portfolio attributes, you must provide the `projectConfig`.
```swift Swift wrap theme={null}
let sdkConfig = MoEngageSDKConfig(appId: "YOUR_WORKSPACE_ID", dataCenter: DATA_CENTER)
sdkConfig.projectConfig = MoEngageProjectConfig(projectID: "YOUR ProjectID")
#if DEBUG
MoEngage.sharedInstance.initializeDefaultTestInstance(sdkConfig)
#else
MoEngage.sharedInstance.initializeDefaultLiveInstance(sdkConfig)
#endif
```
```objective-c Objective C wrap theme={null}
MoEngageSDKConfig* sdkConfig = [[MoEngageSDKConfig alloc] initWithAppId:@"YOUR_WORKSPACE_ID" dataCenter: DATA_CENTER];
sdkConfig.projectConfig = [[MoEngageProjectConfig alloc] initWithProjectID:@"YOUR Project ID"];
#ifdef DEBUG
[[MoEngage sharedInstance] initializeDefaultTestInstance:sdkConfig];
#else
[[MoEngage sharedInstance] initializeDefaultLiveInstance:sdkConfig];
#endif
```
**Validations during initialization**
The MoEngage iOS SDK enforces the following checks while initializing. In `DEBUG` builds (SDK initialized with `initializeDefaultTestInstance(_:)` and the app running under Xcode), each of these throws a fatal exception and crashes the app. In Release and TestFlight builds, the invalid configuration or attribute is dropped silently and logged.
* **Empty Workspace ID.** Initializing `MoEngageSDKConfig` with an empty `appId` is invalid.
* **SDK version downgrade.** Downgrading the MoEngage iOS SDK to a lower version than what the app was previously shipped with is not supported. The SDK detects the downgrade during initialization and crashes in `DEBUG` builds.
* **API calls before initialization.** Calling MoEngage tracking APIs — for example, email click tracking, Live Activity, or analytics methods — before the SDK is initialized throws a fatal exception in `DEBUG` builds. In Release and TestFlight builds, these calls are dropped silently. Initialize the SDK in `application(_:didFinishLaunchingWithOptions:)` before invoking any other MoEngage API.
Following details of the different data centers you need to set based on the dashboard hosts
| Data Center | Dashboard host |
| :------------------------- | :------------------------ |
| DataCenter.DATA\_CENTER\_1 | dashboard-01.moengage.com |
| DataCenter.DATA\_CENTER\_2 | dashboard-02.moengage.com |
| DataCenter.DATA\_CENTER\_3 | dashboard-03.moengage.com |
| DataCenter.DATA\_CENTER\_4 | dashboard-04.moengage.com |
| DataCenter.DATA\_CENTER\_5 | dashboard-05.moengage.com |
| DataCenter.DATA\_CENTER\_6 | dashboard-06.moengage.com |
For more information about the detailed list of possible configurations, refer to the [API reference](https://moengage.github.io/ios-api-reference/Classes/MoEngageSDKConfig.html).
**Note**
***data\_center\_06*** is available from MoEngage-iOS-SDK version 9.17.3 onwards
**Important**
Make sure to call the initialization method in `applicationDidFinishLaunching(_:)` method. In case if you are initializing the SDK at a later stage and not at launch then you will have to call all the notification related methods instead of just relying on [AppDelegate Swizzling](https://www.moengage.com/docs/developer-guide/ios-sdk/push/basic/ios-push-integration-tutorial#app-delegate-method-swizzling).
# Test/Live Environment
As mentioned above while initializing the build, MoEngage SDK makes use of the `DEBUG` preprocessor macro to decide whether the build is meant for TEST/LIVE Environment. Therefore, you will have to take care of the same while generating the build and make sure that the **Build Configuration** of the App's target is set as mentioned below:
* For **Development** Build: Build Configuration should be set to **Debug** (Data will be tracked in **TEST** Environment)
* For **AdHoc Build/App Store** Build: Build Configuration should be set to **Release** (Data will be tracked in **LIVE** Environment)
**What if Build Configuration is not set correctly?**
If the build configuration is not set correctly following might happen:
* You will see the data from the development build in LIVE environment
* **OR** data from AdHoc/Production Build in TEST environment
* You will get **Bad Device Token** error while trying to send a push notification to the device
# How to set Build Configuration?
## Build Configuration on Running the app from Xcode:
Whenever you run the app directly from Xcode without archiving, make sure the build configuration of **Run mode** of the App Target in Edit Scheme is set to **Debug**(set by default settings). Doing this will make sure data is tracked in TEST Environment.
## Build Configuration on Exporting the build:
While exporting the build make sure you set the correct `Build Configuration`. By default for Archive section in `Edit Scheme` the `Build Configuration` will be set to `Release`, but for a development build make sure its changed to `Debug` before exporting the build. To set the **Build Configuration** of the build in your Xcode project go to **App Target > Edit Scheme > Archive > Build Configuration** and set the configuration to **Debug/Release**(depending on the type of build). Refer to the image as described:
# Switching Environment in Dashboard
In the MoEngage dashboard you can switch between [test and live environment](/docs/user-guide/getting-started/initial-setup/dashboard-overview#live-and-test-environments) for your app.
**TEST** Environment is used for all the development and testing-related uses and **LIVE** environment is used for running all the campaigns for AppStore Builds for your app's user base.
# SDK Integration
Source: https://moengage.com/docs/developer-guide/ios-sdk/sdk-integration/basic/sdk-integration
Install the MoEngage iOS SDK using Swift Package Manager or CocoaPods for your project.
Connect your IDE to the [MoEngage docs MCP server](/docs/developer-guide/connect-your-ide-to-moengage-docs) for accurate, context-aware SDK guidance inside your development environment. Your assistant searches this documentation directly, helping you move through the integration with confidence.
**Note**
* The current SDK supports **iOS 13 and above**.
* For complete API reference of the SDK, refer to the docs in this [link](https://moengage.github.io/ios-api-reference/).
Review the [SDK Deprecation Policy](/docs/developer-guide/sdk-lifecycle-and-policies/sdk-deprecation-policy) for supported SDK versions and deprecation timelines before integrating.
# Integration through Swift Package Manager
Swift Package Manager (SPM) is a tool for managing the distribution of Swift code. It’s integrated with the Swift build system to automate the process of downloading, compiling, and linking dependencies.
To install the MoEngage-iOS-SDK through SPM, follow the below steps:
1. Navigate to File -> Add Package
2. Enter the URL [https://github.com/moengage/apple-sdk.git](https://github.com/moengage/apple-sdk.git) for SDK versions equal and above 9.23.0, or [https://github.com/moengage/MoEngage-iOS-SDK.git](https://github.com/moengage/MoEngage-iOS-SDK.git) for other SDK versions and select the branch as master or required version to install the package.
3. Click Add Package.
4. Now, MoEngage-iOS-SDK package is installed.
**Information**
CocoaPods is being deprecated. MoEngage recommends using Swift Package Manager for all new integrations. If your project requires CocoaPods, see the [CocoaPods Integration Guide](/docs/developer-guide/ios-sdk/sdk-integration/basic/integration-through-cocoa-pods).
# Manual Integration
For more information about how to integrate the SDK manually into your project, refer to [Manual Integration](/docs/developer-guide/ios-sdk/manual-integration/manual-integration).
# Troubleshooting and FAQs - iOS SDK
Source: https://moengage.com/docs/developer-guide/ios-sdk/troubleshooting-and-faqs/troubleshooting-and-faqs-ios
Enable SDK logs and find answers to common MoEngage iOS SDK integration and debugging questions.
# Enable SDK logs
By default, we have disabled logs from the SDK. For debugging if you want to see the SDK logs in your console, use the configuration below:
```swift Swift wrap theme={null}
sdkConfig.consoleLogConfig = MoEngageConsoleLogConfig(isLoggingEnabled: true, loglevel: .verbose)
```
```objective-c Objective C wrap theme={null}
MoEngageConsoleLogConfig *consoleLogConfig = [[MoEngageConsoleLogConfig alloc] initWithIsLoggingEnabled:TRUE loglevel:MoEngageLoggerTypeVerbose];
```
*All MoEngage logs are prefixed by the keyword "MoEngage".*
# FAQs
## Don't see events in the dashboard?
* Make sure that the [SDK initialization](/docs/developer-guide/ios-sdk/sdk-integration/basic/sdk-initialization) is done properly. Check if the App ID provided in the initialization method is correct.
* Once the App ID is checked, make sure you are checking in the right MoEngage environment. While initializing the SDK, you must make use of the **DEBUG** macro in your project. By default behavior, if the build configuration is set to Debug then events will show up in **Test Environment**, or else if build configuration is set to Release then events will be tracked in **Live Environment**.
* In the SDK we reject events with invalid event attributes. Event attribute values can only have Strings, Numbers, and dictionaries, or else events might get rejected. For confirming enable the SDK logs and check if the events list sent while the app goes to the background has the event you are tracking, if not then events are getting rejected by the SDK.
* If neither of the above then sometimes it takes a little time to show up in the dashboard, so wait for about 10-15 mins for the events to show up. :)
## Seeing events of one user in another user's profile?
* Make sure you have implemented user unique ID tracking properly. Please set [**User Attribute Unique ID**](/docs/developer-guide/ios-sdk/data-tracking/basic/tracking-user-attributes) **unique** to your users that are logged, don't hardcode this value.
* Make sure [resetUser](/docs/developer-guide/ios-sdk/data-tracking/basic/tracking-user-attributes) method is called on the logout feature of the App so that SDK can differentiate between the users.
## IDFA not getting tracked?
* SDK tracks IDFA (Advertising Identifier) only when `AdSupport` framework is included in the project. This is to make sure to track IDFA only for apps that use `AdSupport` to show ads in the App. Also, there is an AppStore restriction on using IDFA without showing Ads in the app, which may result in rejection of the build.
* Also, we track IDFA only if the user has not limited the Ad Tracking in the device settings.
* If your app is not showing any ads then we advise you to remove `AdSupport` the framework to avoid rejection in the AppStore review process.
## Not getting push notifications on your devices?
* First, check if the [implementation](/docs/developer-guide/ios-sdk/push/basic/ios-push-integration-tutorial#step-1-implement-notification-service-extension-nse) is done correctly. Ensure you registered your app for push notifications correctly, check if you are getting the device token while trying to register for Remote Notification, and the same is sent to the SDK correctly.
* Refer this [link](/docs/developer-guide/ios-sdk/push/basic/apns-certificate-pem-file-legacy) and verify uploaded APNS pem file is valid.
* The dashboard says notification sent, but the device didn’t receive it - Check if notification settings for the app were disabled.
## Getting BadDeviceToken Error while sending the push notifications?
* This error comes whenever you try to send push notifications from the TEST environment to AppStore/AdHoc Build **OR** from a LIVE environment to development build.
* Set Build Configuration correctly and test in the correct environment. Refer [link](/docs/developer-guide/ios-sdk/sdk-integration/basic/sdk-initialization) for more info.
## Getting DeviceTokenNotForTopic Error while sending the push notifications?
* You will get this error when **Bundle-ID** of the build-in which you are expecting the push notifications and that of APNS certificate uploaded in the dashboard (`pem` file) are different.
## Push notification clicks not getting updated?
* Ensure that you are calling MoEngage SDK methods in callback methods which are called on receiving remote notifications.
## Rich landing not working for push notifications?
* **If HTTP link**: HTTP URLs aren't supported in iOS9 and above unless explicitly specified in the plist. Include **App Transport Security Settings** Dictionary in your **Info.plist** and inside this set **Allow Arbitrary Loads to YES**.
* Check if the Rich Landing URL is valid.
## Images/Video/Audio in Rich Notifications(iOS10 and above) not showing in push notifications?
* Make sure you have implemented the [Rich Notification](/docs/developer-guide/ios-sdk/push/basic/ios-push-integration-tutorial#tracking-and-analytics-verification) feature correctly.
* **If HTTP link**: HTTP URLs aren't supported in iOS9 and above unless explicitly specified in the plist. Include **App Transport Security Settings** Dictionary in your Notification Service Extensions **Info.plist** and inside this set **Allow Arbitrary Loads to YES**.
* Check if the Image/Audio/Video URL is valid.
* **If only the video doesn't display**: Check that the video URL uses HTTPS and that the file is within Apple's attachment size limit for video. The NSE doesn't report an error, so the notification is still delivered with its title and body. Refer to [Media Requirements](/docs/developer-guide/ios-sdk/push/basic/ios-push-integration-tutorial#media-requirements).
* **If the image is in WebP (`.webp`) format**: iOS supports only [the formats listed by Apple for notification attachments](https://developer.apple.com/documentation/usernotifications/unnotificationattachment#Supported-File-Types), which don't include WebP. Use JPG, PNG, or GIF.
## Not Getting notifications that are not clicked by users in the Notification Center(Inbox)?
* Make sure that the SDK Version is above 4.4.0, and confirm if you have implemented Notification Service Extension as mentioned [here](/docs/developer-guide/ios-sdk/push/basic/ios-push-integration-tutorial#step-1-implement-notification-service-extension-nse).
* Along with it make sure that the App Group IDs are set correctly for both the [App Target](/docs/developer-guide/ios-sdk/push/basic/ios-push-integration-tutorial#step-1-implement-notification-service-extension-nse) and the Service Extension target.
## Not getting Screen Names drop-down in the Navigation Action while creating the campaign?
* MoEngage iOS SDK doesn't track screen Names in your app. The possible values for screenName parameter are something that has to be defined by developers in the project. Therefore, there will be no dropdown in case of iOS and the Screen name value has to be entered by the marketer in the text field while creating the campaign. MoEngage will provide the entered value in the notification payload. The developers will have to implement the part to parse and get the `screenName` parameter's value and to navigate to the mentioned screen.
## InApp campaign doesn't show in the app?
* Make sure **handleInAppMessage** method is called wherever In-App is displayed - [link](/docs/developer-guide/ios-sdk/in-app-messages/in-app-nativ)
* In-App only came once and can’t see/test it again. Then it might be a case where inApp to be shown may not pass a set of rules which we have kept in SDK to make sure the user experience isn't hindered. Refer the [link](/docs/developer-guide/ios-sdk/in-app-messages/in-app-nativ) to know about these rules.
* In case image URLs are with HTTP scheme: HTTP URL aren't supported in iOS9 and above unless explicitly specified in the Apps `info.plist`. You will have to include **App Transport Security Settings** Dictionary in your Info.plist and inside this set **Allow Arbitrary Loads** to **YES**.
* [Enable SDK logs](#enable-sdk-logs) to get more details on why inApp didn't show up and share it with MoEngage Team.
## Why are you getting Missing Purpose String in Info.plist File warning from App Store?
Very recently you would have started getting this warning mail from Apple on uploading the build to AppStore. This mail informs about a restriction that will be applied by Apple starting from Spring 2019. As a fix for this issue, we have separated SDK's Geofence module to [MOGeofence](http://cocoapods.org/pods/MOGeofence) from MoEngage-iOS-SDK [version 4.3.0](/docs/release-notes/sdks/ios), and going forward has to be integrated separately into a project. Also because of this, there are few changes in the implementation of geofence campaigns. Please follow the [docs](/docs/developer-guide/ios-sdk/push/optional/location-triggered) here to know more.
# Getting FCM Server Key
Source: https://moengage.com/docs/developer-guide/partner-integrations/firebase/getting-fcm-server-key
Locate your Firebase Cloud Messaging server key in the Firebase console for MoEngage integration.
Firebase Cloud Messaging (FCM) server key is available on the Firebase console.
For more information, refer to [Firebase documentation](https://firebase.google.com/docs/projects/api-keys).
# Content Security Policy (CSP) and Impact on Personalize
Source: https://moengage.com/docs/developer-guide/personalize-sdk/sdk-integration/content-security-policy-csp-and-impact-on-personalize
Learn how to configure CSP to work seamlessly with MoEngage Personalize.
# What is Content Security Policy
**Content Security Policy** (CSP) for websites is a security mechanism that allows web administrators to define and enforce rules for how resources (such as scripts, stylesheets, images, and fonts) are loaded and executed on their websites. It helps protect against cross-site scripting (XSS) attacks and other types of code injection vulnerabilities.
CSP works by specifying an HTTP header or a meta tag in the website's HTML code, which contains a policy directive defining the allowed sources for various types of content. These sources can include hostnames, paths, or specific types of content (e.g., `'self'` for the same origin, `'none'` for disallowed, `'unsafe-inline'` for inline scripts/styles, and `'unsafe-eval'` for evaluating code dynamically).
```html HTML wrap theme={null}
```
In this example:
* `default-src 'self';` sets the default policy for all resource types to only allow resources from the same origin ('self').
* `script-src 'self' 'unsafe-inline' www.example.com;` allows scripts to be loaded from the same origin ('self'), allows inline scripts ('unsafe-inline'), and allows scripts from [www.example.com](http://www.example.com/).
* `style-src 'self' 'unsafe-inline' fonts.googleapis.com;` allows stylesheets to be loaded from the same origin ('self'), allows inline styles ('unsafe-inline'), and allows stylesheets from [fonts.googleapis.com](https://developers.google.com/fonts).
* `img-src 'self' data:;` allows images to be loaded from the same origin ('self') and allows data URLs.
* `font-src 'self' fonts.gstatic.com;` allows fonts to be loaded from the same origin ('self') and allows fonts from fonts.gstatic.com.
***
# Why is CSP important
When a user visits a website with CSP enabled, their browser will check if the requested resources comply with the defined policy. If any resources do not meet the policy's rules, the browser may block or modify their behavior, depending on the configuration.
CSP provides several benefits, including:
1. **Mitigation of XSS attacks**: By restricting the sources of executable code, CSP reduces the risk of malicious code injection by only allowing trusted sources.
2. **Protection against data exfiltration**: CSP can prevent unauthorized data transmissions by limiting the origins to which data can be sent.
3. **Protection against clickjacking**: CSP can prevent clickjacking attacks by restricting the frame or iframe sources.
4. **Enhanced security posture**: CSP helps in fortifying the overall security of web applications and websites by providing an additional layer of protection against various types of attacks.
***
# How does it impact Personalize
If you have implemented a Content Security Policy (CSP) for your website, which specifies the allowed sources for loading your website's content, browsers will reject content from sources that are not whitelisted. In this scenario, browsers will not permit MoEngage to load content on your website. As a result, the loading of variations that you have created in MoEngage may be affected.
***
# How to navigate CSP
To enable MoEngage to load variations on your website and generate previews for your variations, you need to whitelist MoEngage by updating the corresponding rules in your existing Content Security Policy (CSP).
| Policy Directive | What To Add To The Directive? |
| :------------------------------------ | :--------------------------------------------------------------------------------------------------- |
| **script-src-elem** or **script-src** | `*.moengage.com` |
| **style-src** | `'unsafe-hashes' *.moengage.com fonts.googleapis.com` |
| **img-src** | `*.moengage.com` [moe-email-campaigns.s3.amazonaws.com](http://moe-email-campaigns.s3.amazonaws.com) |
| **font-src** | `*.moengage.com fonts.googleapis.com fonts.gstatic.com;` |
| **connect-src** | `*.moengage.com` |
| **frame-src** | `*.moengage.com` |
| **media-src** | `*.moengage.com` |
# Custom Attributes
Source: https://moengage.com/docs/developer-guide/personalize-sdk/sdk-integration/custom-attributes
Learn how to define and use custom attributes to personalize user experiences on your website.
**Prerequisite**
MoEngage Personalize SDK must be integrated on your webpage to define custom attributes.
# What are custom attributes?
Custom attributes are used to define what pages on a website and what values on those pages can be used to personalize the experience for visitors. It helps you avoid configuring a combination of multiple URLs to deliver the experience on the right set of pages.
Below are the set of standard fields that can be used as a targeting criterion:
* `pageType`
* `category`
* `firstLevel`
* `secondLevel`
* `thirdLevel`
* `fourthLevel`
* `unitPrice`
* `salePrice`
* `currency`
Any values apart from the above can be defined in the **custom** block and then used for targeting.
```javascript JavaScript wrap theme={null}
window.moePageContext = {
pageType: "product page",
category: {
firstLevel: "Clothing & Accessories",
secondLevel: "T-shirts",
thirdLevel: "Polos"
},
unitPrice: "24",
salePrice: "18",
discount: "25",
currency: "USD",
custom: {
size: "Medium",
color: "Black",
gender: "Unisex"
}
};
```
**Below are some examples of using custom attributes**
## E-commerce
| Field name | Description | Example |
| :------------ | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | :--------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **pageType** | Define the type of page. Standard page type values are: *Homepage, category, cart, checkout, success* | `pageType: "Homepage"` **OR** `pageType: "category"` |
| **category** | Experiences can be targeted for any of the 4 levels of categories. | `category: { firstLevel: "Electronics", secondLevel: "Home Audio", thirdLevel: "Speaker", fourthLevel: "Bluetooth Speakers" }` |
| **unitPrice** | Target product pages based on the original price of the product. | `unitPrice: "24"` |
| **salePrice** | Target product pages based on the discounted price of the product. | `salePrice: "18"` |
| **discount** | Target product pages based on the discount amount on the product. | `discount: "25"` |
| **currency** | Target product pages based on the currency of the product. | `currency: "USD"` |
| **custom** | Any other trait that you want to target for personalization.
**Example:** Personalize all pages that display **White, Medium**-size Polo T-shirts for **Men** **OR** Personalize checkout page only when the cart value is \$500 or above and cart discount is 0 and cart contains at least 2 items. | **custom**: `{ color: "White", size: "M", gender: "Male" }` **OR** **custom**: `{ cartAmount: "750", cartDiscount: "0", cartQuantity: "4" }` |
## Finance
| Attribute | Description | Example |
| :----------- | :---------------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------- |
| **pageType** | Define the type of page. Standard page type values are: *Homepage, blog, service/products, resources/downloads, offers* | `pageType: "Homepage"` **OR** `pageType: "products"` **OR** `pageType: "services"` |
| **category** | Experiences can be targeted for any of the 4 levels of categories. | `category: { firstLevel: "Insurance", secondLevel: "Health Insurance", thirdLevel: "Medical Insurance" }` |
For further assistance, please contact your MoEngage Customer Success Manager (CSM) or the Support team.
# Offerings events tracking
Source: https://moengage.com/docs/developer-guide/personalize-sdk/sdk-integration/offerings-events-tracking
Track impression and click events for MoEngage Offerings fetched via the Personalize SDK or API.
This document outlines the new methods available in the MoEngage Personalize SDK to report impressions and clicks for Offerings fetched using the [MoEngage Personalize SDK](/docs/developer-guide/personalize-sdk/sdk-integration/self-handled-personalize-api-experiences) or directly via the [MoEngage Personalize API](https://www.moengage.com/docs/api/personalize-experience/personalize-overview).
# Pre-Requisites
## SDK Integration
Refer to [this article](/docs/developer-guide/web-sdk/web-sdk-integration/basic-integration/web-sdk-integration) to integrate the Web SDK on your website.
Refer to [this article](/docs/developer-guide/personalize-sdk/sdk-integration/web-personalization-v2) to integrate the Personalize SDK on your website.
## MoEngage Account Configuration
Ensure your MoEngage workspace is enabled to utilize Offerings. For details on setting up Offerings, refer to [Create Offerings](/docs/user-guide/decisioning/offer-decisioning/create-offerings).
# Reporting Offering Shown events
The SDK provides a method to track offering shown events.
Impressions should be reported when an Offering is visually presented to the user.
To report an impression offering, pass the **offeringContext** as a map.
```javascript Javascript wrap theme={null}
```
# Reporting Offering Clicked events
The SDK provides a method to track offering clicked events.
Clicked events should be reported when a user clicks on any offering contained in the response of the Personalize API. To report a click event for a single offering, pass the **offeringContext** of the offering.
When a user clicks on an Offering, we understand that they are also implicitly clicking on the parent Experience. You can now optionally pass the experienceContext to the **offeringClicked** function.
### **What this means**
* **If you pass both contexts,** MoEngage will **automatically** track clicks for **both** the Offering and the Experience. You no longer need to make a second, separate call to track the click event for the parent experience.
* **If you only pass the offeringContext,** the **experienceContext** is optional. If you don't pass it, we will only track the click for the Offering. You would then need to track the experience click separately, if required.
```javascript Javascript wrap theme={null}
```
# Personalize API Experience events tracking
Source: https://moengage.com/docs/developer-guide/personalize-sdk/sdk-integration/personalize-api-experience-events-tracking
Track impression and click events for API experiences created via the MoEngage Personalize API.
This document outlines the new methods available in the MoEngage Personalize SDK to report impressions and clicks for API experiences created via the [MoEngage Personalize API](https://www.moengage.com/docs/api/personalize-experience/personalize-overview).
# Pre-Requisites
## SDK Integration
1. Refer to [this article](/docs/developer-guide/web-sdk/web-sdk-integration/basic-integration/web-sdk-integration) to integrate the Web SDK on your website.
2. Refer to [this article](/docs/developer-guide/personalize-sdk/sdk-integration/web-personalization-v2) to integrate the Personalize SDK on your website.
## MoEngage Account Configuration
Ensure your MoEngage workspace is enabled to utilize Personalize. Refer to [this article](https://www.moengage.com/docs/user-guide/personalize/server-side-personalization/create-server-side-personalization-experience) for details on setting up a Personalize API experience.
# Reporting Experience Shown events
The SDK provides a method to track experience shown events. To report an impression for an experience, use the below SDK method.
```javascript Javascript wrap theme={null}
```
**experienceContext** is a JSON object that is returned in the Response to the Personalize API experience Fetch call. More details [here](https://www.moengage.com/docs/api/experiences/fetch-experience#response-experiences).
# Reporting Experience Clicked events
The SDK provides a method to track experience clicked events. To report an click for an experience, use the below SDK method.
```javascript Javascript wrap theme={null}
```
**extraAttributes** is a **optional** JSON object in which you can pass additional information about the link or the CTA which the user has interacted with.
```json JSON wrap theme={null}
{
"button_id": "",
"button_name": "",
"button_type": "",
}
```
The example JSON provided above illustrates sample values designed to give you a clear understanding of the types of data you can include when tracking additional information about clicks. Feel free to adapt these values to suit your specific implementation needs.
# fetchExperiences - MoEngage Personalize SDK
Source: https://moengage.com/docs/developer-guide/personalize-sdk/sdk-integration/self-handled-personalize-api-experiences
Fetch personalized experience payloads for the current user and render them using your own frontend components.
# Overview
`fetchExperiences` is a client-side method on the MoEngage Personalize SDK. Call it at runtime typically on page load or component mount - with a list of experience keys, and it returns the personalized content that MoEngage has resolved for the current user.
Experience keys are identifiers configured in the MoEngage dashboard for Personalize API experiences. Each key maps to a placement on your website - a homepage banner, a cart offer slot, a product listing card, and so on. MoEngage evaluates the current user against your active experiences and returns only the experiences the user qualifies for. Experiences that cannot be resolved for the user are returned in a separate `failures` array with a structured reason code.
Your website owns the rendering layer. MoEngage provides the decision and the content payload.
# Implementing fetchExperiences
## Pre-requisites
### SDK installation
1. Refer to [this article](/docs/developer-guide/web-sdk/web-sdk-integration/basic-integration/web-sdk-integration) to integrate the Web SDK on your website.
2. Refer to [this article](/docs/developer-guide/personalize-sdk/sdk-integration/web-personalization-v2) to integrate the Personalize SDK on your website.
### Creating a Personalize API experience
Refer to [this article](https://www.moengage.com/docs/user-guide/personalize/server-side-personalization/create-server-side-personalization-experience) to create a Personalize API experience.
## When to use
Use `fetchExperiences` when your website renders personalized content using its own components and MoEngage provides the content payload and decisioning. Common use cases include personalized banners, offer cards, loyalty modules, product listing promotions, cart upsells, and dynamic recommendation blocks.
Implement through a centralized personalization layer rather than independently from unrelated page components. This makes it easier to manage fallback behaviour, attribution, and experience key governance across your site.
## Method signature
```javascript theme={null}
Moengage.personalize.fetchExperiences(experienceKeys, customAttributes?)
```
| Parameter | Type | Required | Description |
| ------------------ | ---------- | -------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `experienceKeys` | `string[]` | Required | Array of experience key strings. Pass specific keys to evaluate only those experiences. Pass `[]` to evaluate all active experiences - MoEngage returns up to 25 eligible results. |
| `customAttributes` | `Object` | Optional | Key-value pairs for runtime audience decisioning. Omit entirely when not needed - do not pass an empty object. |
The method returns a **Promise** that resolves with a response object containing `experiences` and `failures`.
## Fetching experiences
### Fetch specific experiences
Use this when the page has fixed placements and you know exactly which experience keys to request.
```javascript JavaScript wrap theme={null}
```
### Fetch experiences with custom attributes
Pass custom attributes when your MoEngage audiences use runtime values for targeting - such as locale, page type, or country.
```javascript JavaScript wrap theme={null}
```
### Fetch all eligible active experiences
Pass an empty array to evaluate the current user against all active experiences. MoEngage returns up to 25 eligible results.
```javascript JavaScript wrap theme={null}
```
Use explicit experience keys for fixed placements. Pass empty-array only when your frontend is built to handle a dynamic set of returned experiences - such as a dynamic offer feed or a discovery-style content area. With empty-array mode, your rendering logic must decide where each returned experience belongs, what to do when a required placement is not returned, and how to handle more results than available slots.
## How it works
1. The page initializes the MoEngage SDK and establishes user identity and consent.
2. Your code calls `fetchExperiences` with the relevant experience keys and any custom attributes.
3. MoEngage evaluates the user against active campaign and associated segment rules, control groups, A/B variants, and decision policies.
4. The Promise resolves with a response object containing `experiences` and `failures`.
5. Your code reads, validates, and renders the payload from each resolved experience.
6. Your code passes the `experience_context` and `offering_context` (if present in the response) back to MoEngage impression and click tracking calls.
# Response
## Response structure
The resolved response contains two top-level keys: `experiences` and `failures`. Both can be present in the same response - a single call can return some resolved experiences and some failures simultaneously.
```json theme={null}
{
"experiences": {
"homepage-banner": {
"payload": {
"title": { "value": "Dress Like You Mean It", "data_type": "string" },
"imageURL": { "value": "https://cdn.example.com/banner.jpg", "data_type": "string" },
"ctaText": { "value": "Shop Men's Edit", "data_type": "string" },
"redirectionURL": { "value": "/collections/mens-fashion", "data_type": "string" }
},
"experience_context": {
"cid": "6a268ba7_F_T_WP_AB_2_P_0_AU_9",
"experience": "Home Page Banner Update",
"moe_variation_id": "2",
"audience_name": "Category viewed - Mens Fashion",
"audience_id": "9",
"moe_locale_id": "0",
"type": "Web Personalization",
"experience_type": "API based Experience"
}
}
},
"failures": [
{
"code": "E003",
"msg": "user not in segment",
"keys": ["cart-offers"]
}
]
}
```
## Response fields
| Field | Description |
| ------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `experiences` | Map of resolved experiences keyed by experience key string. Only experiences the user qualifies for appear here. |
| `experiences.` | The resolved experience object. Use bracket notation for keys containing hyphens - for example, `result.experiences['homepage-banner']`. Dot notation (`result.experiences.homepage-banner`) treats `-` as the subtraction operator and produces a runtime error that can be difficult to trace. |
| `payload` | Object containing the content fields configured for the experience in MoEngage. Field names match what was configured in the dashboard. |
| `payload..value` | The value for a payload field. May be a direct scalar such as a title or URL, or a serialized JSON string - depending on how the experience is configured. See [Payload formats](#payload-formats). |
| `payload..data_type` | Data type of the field. For example, `string`. |
| `experience_context` | Campaign-level metadata used for impression and click attribution. **Pass this object as-is to MoEngage tracking calls — do not modify, omit, or restructure any of its fields.** The fields below describe what each value represents, but none of them should be altered by your implementation. See [Personalize API Experience events tracking](/docs/developer-guide/personalize-sdk/sdk-integration/personalize-api-experience-events-tracking). |
| `experience_context.cid` | Composite impression ID used by MoEngage to attribute the event to the correct campaign, variant, and audience. |
| `experience_context.experience` | Name of the experience or campaign as configured in MoEngage. |
| `experience_context.moe_variation_id` | A/B variation the user is assigned to. |
| `experience_context.audience_name` | Name of the matched audience segment. |
| `experience_context.audience_id` | ID of the matched audience segment. |
| `experience_context.type` | Experience channel - for example, `Web Personalization`. |
| `experience_context.experience_type` | Experience implementation type - for example, `API based Experience`. |
| `failures` | Array of experience keys that could not be resolved for the current user. See [Handling failures](#handling-failures). |
| `failures[].code` | Machine-readable failure code. |
| `failures[].msg` | Human-readable failure message. |
| `failures[].keys` | The experience key(s) affected by this failure. |
## Payload formats
The shape of the payload depends on how the experience is configured in MoEngage. There are two formats.
### Format 1 - Direct field values
Each content field is returned as a separate key under `payload`. Read values directly - no parsing required.
```json theme={null}
{
"payload": {
"title": { "value": "Dress Like You Mean It", "data_type": "string" },
"imageURL": { "value": "https://cdn.example.com/banner.jpg", "data_type": "string" },
"ctaText": { "value": "Shop Men's Edit", "data_type": "string" },
"redirectionURL": { "value": "/collections/mens-fashion", "data_type": "string" }
}
}
```
Read values directly using optional chaining:
```javascript JavaScript wrap theme={null}
```
Use bracket notation for experience keys that contain hyphens - `result.experiences['homepage-banner']`. Dot notation (`result.experiences.homepage-banner`) treats `-` as the subtraction operator and produces a runtime error that can be difficult to trace.
### Format 2 - Serialized JSON offer array
One payload field contains a JSON-encoded string representing a ranked list of offers returned by a Decision Policy. Parse it with `JSON.parse()` before use.
```json theme={null}
{
"payload": {
"promo_card": {
"value": "[{\"dp_offering_id\":\"...\",\"offering_content\":{\"payload\":{\"title\":\"Voucher Zone\",\"cTAText\":\"Explore\",\"imageURL\":\"https://...\"}}}]",
"data_type": "string"
}
}
}
```
```javascript JavaScript wrap theme={null}
```
Do not call `JSON.parse()` on every payload field. Parse only fields you know contain serialized JSON. Calling `JSON.parse()` on a plain string value such as a title or URL will throw an error.
## Handling failures
When an experience key appears in `failures`, it was not served to the current user. Your implementation should suppress the placement or render default content.
| Code | Reason | Recommended handling |
| ------ | ----------------------------------------- | ------------------------------------------- |
| `E001` | User is in campaign control group | Suppress placement or serve default content |
| `E002` | User is in global control group | Suppress placement or serve default content |
| `E003` | User is not in the segment | Suppress placement or serve default content |
| `E004` | Invalid experience key | Log as a configuration error |
| `E005` | Maximum limit breached for experience key | Log as a configuration error |
| `E006` | Experience is not active | Log as a configuration error |
| `E007` | Experience is expired | Log as a configuration error |
| `E008` | Personalization failed for user | Serve default/fallback content |
E001–E003 and E008 are expected runtime states. E004–E007 indicate a configuration issue in MoEngage and should trigger a log entry or alert for investigation.
```javascript JavaScript wrap theme={null}
```
# Implementation examples
## Full example - reading a direct field-value payload
The click handler below uses `attachClickHandler()` as a sample reference. Replace this with whichever event binding pattern your frontend framework or component library uses.
```javascript JavaScript wrap theme={null}
```
## Full example - reading a serialized JSON offer array payload
When an experience payload contains offering data (Format 2), each offer in the array has its own `offering_context`. Track an impression and click for each offer that is shown or interacted with.
Refer to [Offerings events tracking](https://www.moengage.com/docs/developer-guide/personalize-sdk/sdk-integration/offerings-events-tracking) for the full method reference.
The click handler below uses `attachClickHandler(offer, index)` as a sample reference. Replace this with whichever event binding pattern your frontend framework or component library uses.
```javascript JavaScript wrap theme={null}
```
## Tracking impressions and clicks
Call the appropriate MoEngage SDK tracking methods after rendering each resolved experience and its offers.
* For every resolved experience key, call `Moengage.personalize.trackImpression(experience_context)` after rendering.
* When a user interacts with the rendered experience, call `Moengage.personalize.trackClick(experience_context, extraAttributes)`. `extraAttributes` is optional — use it to pass additional context about the CTA clicked.
* For experiences that contain offering data, call `Moengage.personalize.offeringShown(offering_context)` for each offer rendered. Each offer in the array has its own `offering_context` — pass it as-is and do not reuse contexts across offers.
* On offer click, call `Moengage.personalize.offeringClicked(offering_context, experience_context)`. Passing both contexts tracks the click for the offering and the parent experience in a single call.
Refer to [Personalize API Experience events tracking](https://www.moengage.com/docs/developer-guide/personalize-sdk/sdk-integration/personalize-api-experience-events-tracking) and [Offerings events tracking](https://www.moengage.com/docs/developer-guide/personalize-sdk/sdk-integration/offerings-events-tracking) for the full method reference.
For further assistance, please contact your MoEngage Customer Success Manager (CSM) or the Support team.
# Web Personalization - V2
Source: https://moengage.com/docs/developer-guide/personalize-sdk/sdk-integration/web-personalization-v2
Integrate the MoEngage Personalize SDK to deliver personalized website experiences to your visitors.
Connect your IDE to the [MoEngage docs MCP server](/docs/developer-guide/connect-your-ide-to-moengage-docs) for accurate, context-aware SDK guidance inside your development environment. Your assistant searches this documentation directly, helping you move through the integration with confidence.
MoEngage's Web Personalization allows you to personalize the website experience for every visitor with limited or no involvement from tech teams. Ensure that you have followed the SDK integration [doc](/docs/developer-guide/web-sdk/web-sdk-integration/basic-integration/web-sdk-integration) and that the SDK is working properly.
# Prefetch the MoE domain
Add the following code snippet at the top of the `` to prefetch the moengage.com domain:
```javascript JavaScript wrap theme={null}
```
The 'X' in the API Endpoint URL refers to the MoEngage data center (DC). MoEngage hosts each customer in a different DC. You can find your DC number (value of X) and replace the value of 'X' in the URL by referring to the DC and API endpoint mapping [here](https://www.moengage.com/docs/user-guide/data/key-concepts/data-centers-in-moengage).
# Method 1: Initiate Web P Module
The following table contains the details of the different data centers mapped to the dashboard hosts and is required for integration.
| Data Center | Dashboard host |
| :---------- | :------------------------ |
| dc\_1 | dashboard-01.moengage.com |
| dc\_2 | dashboard-02.moengage.com |
| dc\_3 | dashboard-03.moengage.com |
| dc\_4 | dashboard-04.moengage.com |
| dc\_6 | dashboard-06.moengage.com |
## Production or LIVE Environment
Add this script to initiate the web personalization module:
```javascript JavaScript wrap theme={null}
```
Change the following fields:
* Replace the 'workspace\_id' in the URL above with the Workspace ID in the MoEngage Dashboard settings. Navigate to Dashboard -> Settings -> App -> General and copy the Workspace ID.
* Replace 'DC' in the URL above with the data center your instance is hosted on. MoEngage hosts each customer in a different data center. Tracked data is stored in the default data center of MoEngage. To find your data center mapping, refer to Data Center Mapping. The possible value is dc\_X where 'X' is your data center number.
* Replace 'sdkVersion' in the URL above with the version of Web SDK that you are using. In case you are using NPM, use the version number of @moengage/web-sdk dependency that is in your package.json file.
For example, if the dashboard URL starts with [https://dashboard-01.moengage.com](https://dashboard-01.moengage.com), your current version of Web SDK is 2, and workspace\_id is YOUR\_WORKSPACE\_ID, then the prefetch and initiation code would be:
```javascript JavaScript wrap theme={null}
```
**Warning**
Always update the version in the web personalisation cdn link whenever you change the version of Web SDK. Both this link and the Web SDK should have the exact same version every time for proper functioning of Web Personalisation.
## Test Environment
For data to be tracked in the test environment, include `debug_logs` in the src of the above script as follows:
```javascript JavaScript wrap theme={null}
```
For example, if the dashboard URL starts with [https://dashboard-01.moengage.com](https://dashboard-01.moengage.com), your current version of Web SDK is 2, and workspace\_id is YOUR\_WORKSPACE\_ID, then the whole code will look like:
```javascript JavaScript wrap theme={null}
```
# Method 2: If you have passed the useLatest: true flag in NPM initialisation config
In this case, Add this script to initiate the web personalization module:
```javascript JavaScript wrap theme={null}
```
Change the following fields:
* Replace the 'workspace\_id' in the URL above with the Workspace ID in the MoEngage Dashboard settings. Navigate to Dashboard -> Settings -> App -> General and copy the Workspace ID.
* Replace 'DC' in the URL above with the data center your instance is hosted on. MoEngage hosts each customer in a different data center. Tracked data is stored in the default data center of MoEngage. To find your data center mapping, refer to Data Center Mapping. The possible value is dc\_X where 'X' is your data center number.
For example, if the dashboard URL starts with [https://dashboard-01.moengage.com](https://dashboard-01.moengage.com), and workspace\_id is YOUR\_WORKSPACE\_ID, then the prefetch and initiation code would be:
```javascript JavaScript wrap theme={null}
```
# Cache Data Refresh
Web Personalization data will be fetched and stored in the browser cache. The data will be synced again on the Next Page Load only if it meets any of the below conditions:
* 15 minutes have passed since the last data fetch
* Login or Logout is executed
# Web Personalization Anti-Flicker Code
The Anti-Flicker snippet is a small piece of code that helps to maintain the user experience on your website when running a web personalisation experience. [Know more about Flicker](https://www.moengage.com/docs/user-guide/personalize/things-to-know/how-to-manage-flicker-on-websites)
Page flickering is a problem that only occurs with experiments on a web page in areas that are visible when the page initially loads. There are certain cases where page flickering may not be an issue, such as experiments occurring below the visible area or experiments that occur after a visitor performs a specific action, such as triggering a Recently Viewed or Added to Cart product grids. In such scenarios, generally, the anti-flicker code need not be added to the website.
## Steps To Install Anti-flicker Code
To prevent page-flickering, there are 2 options
1. Masking the content of the entire page until the personalization is complete **OR**
2. Masking individual elements that have been personalized using the WYSIWYG editor
**Note**
* Only one of approaches - [Option 1](/docs/developer-guide/personalize-sdk/sdk-integration/web-personalization-v2#option-1-masking-content-of-the-entire-webpage) or [Option 2](/docs/developer-guide/personalize-sdk/sdk-integration/web-personalization-v2#option-2-masking-individual-elements-on-the-webpage) - must be implemented on your website.
### Option 1: Masking content of the entire webpage
1. Add the below code as high on the page as possible, in the `` of your website code.
2. In the script below, **MAX\_RENDER\_TIME** refers to the wait time (in milliseconds) before the visitor is shown the original website in case the personalized experience fails to load. You can update this value based on your website performance needs.
```javascript JavaScript wrap theme={null}
```
### Option 2: Masking individual elements on the webpage
This method will only mask specific elements on the webpage that have been personalized. In the script below,
1. Add HTML selector of each personalized element on the webpage to the array **personalizedSelectors.** [How to find HTML selectors for personalized elements?](/docs/developer-guide/personalize-sdk/sdk-integration/web-personalization-v2#option-1-use-the-element’s-html-id)
2. **maxRenderTime** refers to the wait time (in milliseconds) before the visitor is shown the original website in case the personalized experience fails to load. You can update this value based on your website performance needs.
3. Add the below code as high on the page as possible, in the `` section of your website code.
```javascript JavaScript wrap theme={null}
```
An example of multiple elements personalized on a webpage is shown below for reference.
```javascript JavaScript wrap theme={null}
const personalizedSelectors = ['#homepage-title', '#homepage-subtitle', '#homepage-cta', '#homepage-cta-link', '#homepage-banner'];
```
There are 2 ways to find the HTML selector that has been personalized.
#### Option 1: Use the element’s HTML ID
1. Right-click on the element.
2. Select **Inspect** or **Inspect Element** in the context menu.
3. In the highlighted HTML, find the HTML ID for that element. This is how the input would look like for the element highlighted in the image.
\
Below is the input to be given to the anti-flicker snippet for the HTML element in consideration.
```javascript JavaScript wrap theme={null}
const personalizedSelectors = ['#homepage-title'];
```
#### Option 2: Use the element’s HTML selector path
1. Right-click on the element.
2. Select **Inspect Element**in the context menu.
3. In the highlighted HTML, right click and choose **Copy Selector** in the context menu.
\
Below is the input to be given to the anti-flicker snippet for the HTML element in consideration
```javascript JavaScript wrap theme={null}
const personalizedSelectors = ['#side-container > a > img'];
```
# Cards Data Payload
Source: https://moengage.com/docs/developer-guide/react-native-sdk/cards/cards-data-payload
Review the data models and payload structure returned by the MoEngage React Native Cards SDK.
# SyncCompleteData
```typescript TypeScript theme={null}
/**
* Data on API sync complete.
*/
class SyncCompleteData {
/**
* Indicating if there were any updates in the cards post sync. true if there are any new
* updates present else false. This value is true even if card(s) are deleted.
*/
hasUpdates: boolean;
/**
* Condition under which sync was triggered. Refer to {@link SyncType}
*/
syncType: SyncType;
}
```
# CardInfo
```typescript TypeScript theme={null}
/**
* All data for cards.
*/
class CardInfo {
/**
* True if showing ALL tabs is enabled else false.
*/
shouldShowAllTab: boolean;
/**
* All configured categories for cards.
*/
categories: Array;
/**
* All cards which are eligible for display currently.
*/
cards: Array;
/**
* Accessibility data for static images used in cards.
*
* @since 6.0.0
*/
staticImageAccessibilityData: { [key in StaticImageType]: MoEAccessibilityData } | null;
}
```
# Card
```typescript TypeScript theme={null}
/**
* Card data
*/
class Card {
/**
* Internal SDK identifier.
*/
id: number;
/**
* Unique identifier for the campaign
*/
cardId: string;
/**
* Category to which the campaign belongs.
*/
category: string;
/**
* Template payload for the campaign.
*/
template: Template;
/**
* Meta data related to the campaign like status, delivery control etc.
*/
metaData: MetaData;
}
```
# Template
```typescript TypeScript theme={null}
/**
* Card Template data
*/
class Template {
/**
* Type of Template.
*/
templateType: TemplateType;
/**
* Containers in the template.
*/
containers: Array;
/**
* Additional data associated to the template
*/
kvPairs: { [k: string]: any };
}
```
# TemplateType
```typescript TypeScript theme={null}
/**
* Supported template types.
*/
enum TemplateType {
/**
* Basic Template
*/
BASIC,
/**
* Illustration Template
*/
ILLUSTRATION
}
```
# Container
```typescript TypeScript theme={null}
/**
* Container to hold UI widget
*/
class Container {
/**
* Unique identifier for a template
*/
id: number;
/**
* Type of container.
*/
templateType: TemplateType;
/**
* Style associated to the Container
*/
style: ContainerStyle | undefined;
/**
* Widget list associated to the Container
*/
widgets: Array;
/**
* Actions to be performed on widget click
*/
actionList: Array;
}
```
# Widget
```typescript TypeScript theme={null}
/**
* UI element in a card.
*/
class Widget {
/**
* Identifier for the widget.
*/
id: number;
/**
* Type of widget
*/
widgetType: WidgetType;
/**
* Content to be loaded in the widget.
*/
content: string;
/**
* Style associated with the widget
*/
style: WidgetStyle | undefined;
/**
* Actions to be performed on widget click
*/
actionList: Array;
/**
* Accessibility data for the widget
* @since 6.0.0
*/
accessibilityData: MoEAccessibilityData | null;
}
```
# WidgetType
```typescript TypeScript theme={null}
/**
* Types of UI widgets supported.
*/
enum WidgetType {
/**
* Widget that loads an image or gif
*/
IMAGE,
/**
* Widget that loads text content.
*/
TEXT,
/**
* Widget that loads button content.
*/
BUTTON
}
```
# MetaData
```typescript TypeScript theme={null}
/**
* Meta data related to a campaign.
*/
class MetaData {
/**
* True if the campaign should be pinned to the top else false.
*/
isPinned: boolean;
/**
* True if the campaign hasn't been delivered to the inbox, else false.
*/
isNewCard: boolean;
/**
* Current state of the campaign.
*/
campaignState: CampaignState;
/**
* Time at which the campaign would be deleted from local store
*/
deletionTime: number;
/**
* Delivery Controls defined during campaign creation.
*/
displayControl: DisplayControl;
/**
* Additional meta data regarding campaign used for tracking purposes.
*/
metaData: { [k: string]: any };
/**
* Creation time for the campaign.
* Notes: Available in iOS, default value is -1
*/
createdAt: number;
/**
* Last time the campaign was updated.
*/
updatedTime: number;
/**
* Complete Campaign payload.
*/
campaignPayload: { [k: string]: any };
}
```
# CardsData
```typescript TypeScript theme={null}
/**
* Data for cards
*/
class CardsData {
/**
* Category for the cards
*/
category: string;
/**
* [List] of [Card]
*/
cards: Array;
/**
* Accessibility data for static images used in cards.
*
* @since 6.0.0
*/
staticImageAccessibilityData: { [key in StaticImageType]: MoEAccessibilityData } | null;
}
```
# SyncType
```typescript TypeScript theme={null}
/**
* Condition/Situation when sync
*/
enum SyncType {
/**
* Sync when application comes to foreground
*/
APP_OPEN,
/**
* Sync when inbox screen opened.
*/
INBOX_OPEN,
/**
* Sync when SwipeToRefresh widget is pulled.
*/
PULL_TO_REFRESH
/**
* Sync when user logs in
* @since 5.0.0
*/
IMMEDIATE
}
```
# StaticImageType
```typescript TypeScript theme={null}
/**
* Enum representing different types of static images used in the cards.
*
* @since 6.0.0
*/
enum StaticImageType {
/**
* Image for No cards/Empty State
*/
EMPTY_STATE
/**
* Pin Card image
*/
PIN_CARD
/**
* Place Holder image for card loading
*/
LOADING_PLACE_HOLDER
}
```
# AccessibilityData
```typescript TypeScript theme={null}
/// @since 12.0.0 of react-native-moengage package
class MoEAccessibilityData {
/// The accessibility text
text: string | null;
/// The accessibility hint
hint: string | null;
}
```
# Framework Initialization
Source: https://moengage.com/docs/developer-guide/react-native-sdk/cards/initialization/framework-initialization
Initialize the MoEngage Cards plugin in your React Native app after the component mounts.
# Cards Plugin Initialization
Initialize the Cards plugin after the component is mounted.
```typescript TypeScript theme={null}
import ReactMoEngageCards from "react-native-moengage-cards";
ReactMoEngageCards.initialize(YOUR_WORKSPACE_ID);
```
There is no platform-specific initialization required.
# Android
Source: https://moengage.com/docs/developer-guide/react-native-sdk/cards/installation/android
Configure Android dependencies for the MoEngage React Native Cards plugin in your project.
In the ***react-native-moengage-cards*** version 10.x.x, the native dependency is part of the Cards plugin itself, so there is no need to include any additional dependency for supporting Cards.
# Framework Dependency
Source: https://moengage.com/docs/developer-guide/react-native-sdk/cards/installation/framework-dependency
Install the MoEngage Cards plugin for React Native using the npm package manager.
Install MoEngage's Cards Plugin to your application, using the npm package manager.

```shell Shell theme={null}
$ npm install react-native-moengage-cards
```
After installing the plugin, use the following platform-specific configuration.
* [Android](/docs/developer-guide/react-native-sdk/cards/installation/android)
* [iOS](/docs/developer-guide/react-native-sdk/cards/installation/ios)
This plugin is dependent on `react-native-moengage` plugin. Make sure you have installed the `react-native-moengage` plugin as well. Refer to the [documentation](/docs/developer-guide/react-native-sdk/sdk-integration/react-native/sdk-installation/framework-dependency) for the same.
# iOS
Source: https://moengage.com/docs/developer-guide/react-native-sdk/cards/installation/ios
Set up iOS dependencies for the MoEngage React Native Cards plugin with architecture support.
We now offer support for turbo architecture starting from version 3.0.0.
To run the application in the new react architecture, follow these steps:
1. Navigate to the iOS folder.
2. Run the command ***RCT\_NEW\_ARCH\_ENABLED=1 bundle exec pod install*** to install the necessary dependencies.
To run the application in the old react architecture, follow these steps:
1. Navigate to the iOS folder.
2. Run the command ***pod install*** to install the necessary dependencies.
# Self Handled Cards
Source: https://moengage.com/docs/developer-guide/react-native-sdk/cards/self-handled-cards
Build custom card views in your React Native app using the MoEngage self-handled cards SDK and APIs.
Self-handled cards give you the flexibility of creating Card Campaigns on the MoEngage Platform and displaying the cards anywhere inside the application. SDK provides APIs to fetch the campaign's data using which you can create your own view for cards.
# Get Cards Info
Fetch All the cards campaign data that are eligible to show for the particular user which returns data as ***CardsInfo***.
```typescript TypeScript theme={null}
import ReactMoEngageCards from "react-native-moengage-cards";
const cardsInfo = await ReactMoEngageCards.getCardsInfo();
```
Refer to the [Cards Data Payload](https://www.moengage.com/docs/developer-guide/react-native-sdk/cards/cards-data-payload) to get the details about the available data in ***CardsInfo***.
## Widget and Widget ID Mapping
### Basic Card/Illustration Card
| Widget Id | Widget Type | Widget Information |
| --------- | -------------------------- | --------------------------------- |
| 0 | Image (WidgetType.IMAGE) | Image widget in the card. |
| 1 | Text (WidgetType.TEXT) | Header text for the card. |
| 2 | Text (WidgetType.TEXT) | Message text for the card. |
| 3 | Button (WidgetType.Button) | Call to action(CTA) for the card. |
# Refresh Cards
Use the ***refreshCards***\*()\*\*\* API to refresh cards on the User Demand. This API can be used to mimic Pull to refresh behavior.
```typescript TypeScript theme={null}
import ReactMoEngageCards from "react-native-moengage-cards";
ReactMoEngageCards.refreshCards((data) => {});
```
Refer to the [Cards Data Payload](https://www.moengage.com/docs/developer-guide/react-native-sdk/cards/cards-data-payload)to get the detail about the available data in ***SyncCompleteData**.*
# Fetch Cards
Use the ***fetchCards***\*()\*\*\* API to fetch cards for the User. This API can be used to sync the latest cards data.
```typescript TypeScript theme={null}
import ReactMoEngageCards from "react-native-moengage-cards";
const cardsData = await ReactMoEngageCards.fetchCards();
```
Refer to the [Cards Data Payload](https://www.moengage.com/docs/developer-guide/react-native-sdk/cards/cards-data-payload)to get the details about the available data in ***CardsData**.*
For details on the sync timing and rate limits for `fetchCards()`, see [When Does the MoEngage SDK Sync Card Data?](/docs/user-guide/campaigns-and-channels/cards/faqs-cards/when-does-the-moengage-sdk-sync-card-data)
# Inbox Loaded
You can show the cards on a separate screen or a section of the screen. When the cards screen/section is loaded call ***onCardSectionLoaded()***.
```typescript TypeScript theme={null}
import ReactMoEngageCards from "react-native-moengage-cards";
ReactMoEngageCards.onCardSectionLoaded((data) => {});
```
Refer to the [Cards Data Payload](https://www.moengage.com/docs/developer-guide/react-native-sdk/cards/cards-data-payload)to get the details about the available data in ***SyncCompleteData**.*
# Inbox UnLoaded
Call ***onCardSectionUnloaded()*** when the screen/section is no longer visible or going to the background.
```typescript TypeScript theme={null}
import ReactMoEngageCards from "react-native-moengage-cards";
ReactMoEngageCards.onCardSectionUnLoaded();
```
# Fetch Categories
To fetch all the categories for which cards are configured, use the***getCardsCategories()*** API. It will return an array of strings.
```typescript TypeScript theme={null}
import ReactMoEngageCards from "react-native-moengage-cards";
const cardsCategories = await ReactMoEngageCards.getCardsCategories();
```
# All Cards Categories Enabled
To fetch all the categories for which cards are configured, use the***isAllCategoryEnabled()*** API.
```typescript TypeScript theme={null}
import ReactMoEngageCards from "react-native-moengage-cards";
const isAllCategoryEnabled = await ReactMoEngageCards.isAllCategoryEnabled();
```
# Fetch Cards for Categories
To fetch cards eligible for display for a specific category use the ***getCardsForCategory()*** API.
```typescript TypeScript theme={null}
import ReactMoEngageCards from "react-native-moengage-cards";
const cardsData = await ReactMoEngageCards.getCardsForCategory(cardCategory);
```
Refer to the [Cards Data Payload](https://www.moengage.com/docs/developer-guide/react-native-sdk/cards/cards-data-payload)to get the details about the available data in ***CardsData**.*
# Get New Cards Count
To obtain the new cards count use \*\*\*getNewCardsCount()\*\*\*method as shown below:
```typescript TypeScript theme={null}
import ReactMoEngageCards from "react-native-moengage-cards";
const newCardsCount = await ReactMoEngageCards.getNewCardsCount();
```
# Card Shown
Call the ***cardShown()*** API to notify a card is shown to the user.
```typescript TypeScript theme={null}
import ReactMoEngageCards from "react-native-moengage-cards";
ReactMoEngageCards.cardShown(card);
```
# Card Clicked
Call the ***cardClicked()*** API to notify a card is shown to the user.
```typescript TypeScript theme={null}
import ReactMoEngageCards from "react-native-moengage-cards";
ReactMoEngageCards.cardClicked(card, widgetId);
```
# Delete Card
Call the ***deleteCard()*** API to delete a card.
```typescript TypeScript theme={null}
import ReactMoEngageCards from "react-native-moengage-cards";
// Delete a single card
ReactMoEngageCards.deleteCard(card);
// Delete a multiple card, pass the Array
ReactMoEngageCards.deleteCard(cards);
```
# Mark Card Delivered
To track delivery to the card section of the application call the ***cardDelivered()*** API when the cards section of the application is loaded.
```typescript TypeScript theme={null}
import ReactMoEngageCards from "react-native-moengage-cards";
ReactMoEngageCards.cardDelivered();
```
# Get Unclicked Cards Count
To obtain the unclicked cards count use \*\*\*getUnClickedCardsCount()\*\*\*method as shown below.
```typescript TypeScript theme={null}
import ReactMoEngageCards from "react-native-moengage-cards";
const unClickedCard = await ReactMoEngageCards.getUnClickedCardsCount();
```
# Card Sync Listener
## Version 4.0.0 and below.
Set this listener to get a callback for card sync on the App opened. This listener should be set before calling \*\*\*initialize()\*\*\*API. In most cases, this API is not required.
```typescript TypeScript theme={null}
import ReactMoEngageCards from "react-native-moengage-cards";
ReactMoEngageCards.setAppOpenSyncListener((data) => {});
```
## Version 5.0.0 and above.
Set this listener to receive a callback for card sync upon app launch or when user login.This listener should be set before calling \*\*\*initialize()\*\*\*API.
```typescript TypeScript theme={null}
import ReactMoEngageCards from "react-native-moengage-cards";
ReactMoEngageCards.setSyncCompleteListener((data) => {});
```
Refer to the [Cards Data Payload](https://www.moengage.com/docs/developer-guide/react-native-sdk/cards/cards-data-payload)to get the details about the available data in ***SyncCompleteData**.*
# Compliance
Source: https://moengage.com/docs/developer-guide/react-native-sdk/compliance/compliance
Enable or disable data tracking and the MoEngage React Native SDK from the JavaScript layer.
Use the APIs below to control what the MoEngage SDK tracks, based on the consent a user has given.
## Enable or Disable Data Tracking
To stop the SDK from tracking custom events and user attributes, call `disableDataTracking()`.
```javascript JavaScript theme={null}
import ReactMoE from 'react-native-moengage';
ReactMoE.disableDataTracking();
```
The SDK rejects all events and user attributes until you call `enableDataTracking()`. Data tracking is enabled by default.
```javascript JavaScript theme={null}
import ReactMoE from 'react-native-moengage';
ReactMoE.enableDataTracking();
```
## Enable or Disable the SDK
To stop the SDK from tracking any user information or sending any data to MoEngage, call `disableSdk()`.
```javascript JavaScript theme={null}
import ReactMoE from 'react-native-moengage';
ReactMoE.disableSdk();
```
All SDK APIs are non-operational until you call `enableSdk()`. The SDK is enabled by default, so call `enableSdk()` only if you disabled it earlier.
```javascript JavaScript theme={null}
import ReactMoE from 'react-native-moengage';
ReactMoE.enableSdk();
```
## Delete User Data
To delete the current user's profile from the MoEngage server, refer to [Delete User From MoEngage Server](/docs/developer-guide/react-native-sdk/data-tracking/delete-user-from-moengage-server).
# Delete User From MoEngage Server
Source: https://moengage.com/docs/developer-guide/react-native-sdk/data-tracking/delete-user-from-moengage-server
Delete the current user from the MoEngage server using the React Native SDK on Android.
This API is supported from **react-native-moengage** version **8.6.0** and is only available for the Android platform and is a no-operation for other platforms.
To delete the current user from the MoEngage server use ***deleteUser()*** method as shown below, where you will get an instance of ***UserDeletionData***.
```javascript TypeScript theme={null}
import ReactMoE from 'react-native-moengage';
// Below method will return an instance of UserDeletionData
const userDeletionData = await ReactMoE.deleteUser();
```
## UserDeletionData
Below is the model returned on calling the API/method.
```typescript TypeScript theme={null}
/**
* Delete User State Data while deleting the user from MoEngage SDK
* @since 8.6.0
*/
class UserDeletionData {
/**
* Account Data, instance of { MoEAccountMeta }
* @since 8.6.0
*/
accountMeta: MoEAccountMeta;
/**
* User State, true if user delete succeeded else false
* @since 8.6.0
*/
isSuccess: boolean;
}
```
# Enable Advertising Identifier Tracking
Source: https://moengage.com/docs/developer-guide/react-native-sdk/data-tracking/enable-advertising-identifier-tracking
Enable advertising identifier tracking in your React Native app for accurate device analytics.
For accurate analytics around devices and tracking Re-installs, it is essential to track the Advertising Identifier.
## Add Ad Identifier Library
Add the below dependency in the application level ***build.gradle*** file.
```groovy Groovy theme={null}
implementation("com.google.android.gms:play-services-ads-identifier:18.0.1")
```
To enable Advertising Identifier tracking use the *enableAdIdTracking()* method as shown below.
```javascript Javascript theme={null}
import ReactMoE from 'react-native-moengage'
ReactMoE.enableAdIdTracking();
```
Before you enable Advertising Id tracking please ensure the application is complying with the [Google Play Policy](https://support.google.com/googleplay/android-developer/answer/10144311) regarding Advertising Id tracking. Refer to our [help document](https://www.moengage.com/docs/user-guide/data/privacy/android-advertising-id-tracking) for more information on the policy.
In case, you need to disable advertising-id after enabling tracking use the following method.
```javascript Javascript theme={null}
import ReactMoE from 'react-native-moengage'
ReactMoE.disableAdIdTracking();
```
The above APIs are available only starting plugin version 7.4.1. In the older versions, Advertising Identifier tracking is enabled by default.
# Install/Update
Source: https://moengage.com/docs/developer-guide/react-native-sdk/data-tracking/install-update
Differentiate between app installs and updates in your React Native app using MoEngage setAppStatus.
During integration if your app is already on the App Store, MoEngage would need to know whether your app update would be an actual UPDATE or an INSTALL.
Have logic in place to differentiate Install and Update and make use of the `setAppStatus()` method to track the same as described:
```javascript JavaScript theme={null}
import ReactMoE, {
MoEAppStatus,
} from "react-native-moengage";
//For Fresh Install of App
ReactMoE.setAppStatus(MoEAppStatus.Install);
// For Existing user who has updated the app
ReactMoE.setAppStatus(MoEAppStatus.Update);
```
# Setting Unique Id for SDK versions below 11.2.0
Source: https://moengage.com/docs/developer-guide/react-native-sdk/data-tracking/setting-unique-id-for-sdk-versions-below-1120
Set a unique user ID for login and logout in React Native SDK versions below 11.2.0.
# Implementing Login/Logout
* It's important to set the User Attribute Unique ID when a user logs into your app.
* This is to merge the new user with existing user, if any exists, and will help prevent creating of unnecessary/stale users.
* Setting the Unique ID is a critical piece to tie a user across devices and installs/uninstalls as well across all platforms (i.e. iOS, Android, Windows, The Web). Set the **USER\_ATTRIBUTE\_UNIQUE\_ID** attribute as soon as the user is **logged in**. Unique ID can be something like an email ID, a username (unique), or a database ID or any Backend generated ID.
* Do not set this for the user who not logged in.
## Login User
```javascript JavaScript theme={null}
import ReactMoE from 'react-native-moengage'
ReactMoE.setUserUniqueID("abc@xyz.com");
```
## Logout User
The application needs to notify the MoEngage SDK whenever the user is logged out of the application. To notify the SDK, call the API whenever the user is logged out of the application.
```javascript JavaScript theme={null}
import ReactMoE from 'react-native-moengage'
ReactMoE.logout();
```
## Updating User Attribute Unique Id
Use the method ***setAlias()*** to update the user attribute unique id instead of \*setUniqueId()\*with a different value. Using the method ***setUniqueId()*** with a new value creates unintended users in MoEngage.
```JavaScript JavaScript theme={null}
import ReactMoE from 'react-native-moengage'
ReactMoE.setAlias("asd@xyz.com");
```
# Tracking Events
Source: https://moengage.com/docs/developer-guide/react-native-sdk/data-tracking/tracking-events
Track user events and their properties using the MoEngage React Native SDK for campaigns.
SDK adheres to the MoEngage FUP policies. For more information, refer to the [Fair Usage Policy](https://www.moengage.com/docs/user-guide/data/key-concepts/fair-usage-policy-fup).
Tracking events is how you record any actions your users perform, along with any properties that describe the action. Every trackEvent call records a single user action. We recommend that you make your event names human-readable so that everyone on your team can know what they mean instantly.
Every ***trackEvent()*** API expects 2 parameters, event name, and event attributes i.e. instance of ***MoEProperties.***
Add all the additional information which you think would be useful for segmentation while creating campaigns.
For example, the following code tracks a Purchase event of a product. We are including attributes like price, quantity, purchase date, and store location which describe the event we are tracking.
```javascript JavaScript theme={null}
import ReactMoE, {
MoEGeoLocation,
MoEProperties,
} from "react-native-moengage";
let properties = new MoEProperties();
properties.addAttribute("quantity", 1);
properties.addAttribute("product", "iPhone");
properties.addAttribute("currency", "dollar");
properties.addAttribute("price", 699);
properties.addAttribute("new_item", true);
properties.addAttribute("models", ["iPhone15", "iPhone14"]);
properties.addDateAttribute("purchase_date", "2020-06-10T12:42:10Z");
properties.addLocationAttribute(
"store_location",
new MoEGeoLocation(90.00001, 180.00001)
);
ReactMoE.trackEvent("Purchase", properties);
```
* Event names should not contain any special characters other than "\_". It can contain just spaces and an underscore. Also, it should not contain “between”, “greater”, “less”, “in\_the\_last”, “in\_the\_next”, “equal”, “contains”, “starts”, or “is\_not".
* You can not use "moe\_" as a prefix while naming events, event attributes, or user attributes. It is a system prefix and using it might result in periodic blacklisting without prior communication.
* You can not use "user\_id" to name custom attributes. It is a reserved system field, and using it might result in an error.
# Analytics
MoEngage SDK tracks user sessions and application traffic sources.To learn more about how user session and application traffic source tracking works, refer to the following docs:
* [Session and Source Analysis](https://www.moengage.com/docs/user-guide/analyze/analytics/session-and-source/session-and-source-analysis)
* [Advanced Session and Source Analysis](https://www.moengage.com/docs/user-guide/analyze/analytics/session-and-source/advanced-session-and-source-analysis)
With user session tracking we have introduced the flexibility to selectively mark events as non-interactive.
## What is a non-interactive event?
Events that do not affect the session calculation in anyways are called non-interactive events. Non-interactive events have the below properties
* Do not start a new session.
* Do not extend the session.
* Do not have information related to a user session.
## How to mark an event as non-interactive?
To mark an event as a non-interactive call ***setNonInteractive()*** of the ***MoEProperties***instance as shown below:
```javascript JavaScript theme={null}
import ReactMoE, {
MoEGeoLocation,
MoEProperties,
} from "react-native-moengage";
let properties = new MoEProperties();
properties.addAttribute("quantity", 1);
properties.addAttribute("product", "iPhone");
properties.addAttribute("currency", "dollar");
properties.addAttribute("price", 699);
properties.addAttribute("new_item", true);
properties.addAttribute("models", ["iPhone15", "iPhone14"]);
properties.addDateAttribute("purchase_date", "2020-06-10T12:42:10Z");
properties.addLocationAttribute(
"store_location",
new MoEGeoLocation(90.00001, 180.00001)
);
//Marking it as Non Interactive
properties.setNonInteractiveEvent();
ReactMoE.trackEvent("Purchase", properties);
```
# Tracking User Attributes and User Identity
Source: https://moengage.com/docs/developer-guide/react-native-sdk/data-tracking/tracking-user-attributes-and-user-identity
Track user attributes and set identifiers for identity resolution in the React Native SDK.
User attributes are pieces of information you know about a user. They could be demographics such as age and gender, account-specific like plan, or whether a user has seen a particular A/B test variation. User attributes are customer properties you can reference throughout the customer's lifecycle.
## Difference Between User Attributes and User Identifiers
User attributes and user identifiers serve different purposes in MoEngage:
**User Identifiers:**
User identifiers are unique values that persist across multiple sessions and devices, allowing MoEngage to recognise a user as the same individual, even when they switch between different platforms or log in later. This process, known as identity resolution, is crucial for maintaining a unified user profile, providing a consistent user experience, and tracking user behaviour accurately.
Common examples of user identifiers include:
* Email address: A user's email address is a widely used identifier because it is unique to the individual and remains consistent across different platforms.
* Phone number: Similar to email addresses, phone numbers can serve as unique identifiers, especially in mobile applications.
* User ID: MoEngage assigns each user a unique ID upon registration. This ID is used as a reliable identifier within the MoEngage platform.
* Customer ID: In e-commerce and customer relationship management (CRM) systems, a customer ID is assigned to track individual customers across various interactions.
These identifiers are set using the ***identifyUser()*** method.
By default, parameter ***ID*** is the identifier used for your workspaces, unless [Identity resolution](https://www.moengage.com/docs/user-guide/data/user-data/unified-identity-identity-resolution#overview) is enabled and identifiers are activated in your workspace.
**User Attributes**:
Descriptive information about a user that enhances their profile - Used for segmentation, personalisation, and analytics - Examples: name, age, gender, preferences, purchase history - Set using dedicated methods like ***setFirstName()*** or ***setUserAttribute()*** - Help create personalised user experiences
In simple terms, identifiers answer "Who is this user?" while attributes answer "What do we know about this user?"
## Powering MoEngage Features
User attributes and identifiers are crucial for leveraging MoEngage effectively:
* **Segmentation:**
* Use attributes to create targeted user groups based on demographics, behavior, etc.
* Example: Segment users by age, purchase history for specific campaigns.
* **Personalisation:**
* Identifiers ensure consistent user experience across devices.
* Attributes enable tailored content (messages, recommendations).
* Example: Personalise emails with names, recommend relevant products.
* **Analytics:**
* Attributes provide context to user actions and behavior data.
* Analyze conversion rates by segments, feature engagement by demographics.
* Gain deeper insights for data-driven decisions.
By using attributes and identifiers, you can build more relevant and engaging user experiences.
# Implementing Login/Logout
For SDK versions below 11.2.00 refer to this document.
## Login User
**Single Identifier**
If your application relies on a single unique user identifier, such as an email ID for login, use the API below to pass the identifier to the MoEngage SDK
```javascript JavaScript theme={null}
import ReactMoE from 'react-native-moengage';
ReactMoE.identifyUser("abc@xyz.com"); //Pass any unique value for your user
```
**Note**: If a key is not specified, the SDK defaults to `uid`, the unique user identifier in MoEngage.
**Multiple Identifiers**
If your application supports multiple login identifiers, such as an email ID, user ID, or mobile number, pass all relevant identifiers to the SDK using the following function:
```javascript JavaScript theme={null}
import ReactMoE from 'react-native-moengage'
ReactMoE.identifyUser({"uid": "react-native","u_em": "react-native@moengage.com"});
```
Updates are made to SDK functions to improve user identification and session management.
* **Forced Logout**: The MoEngage SDK no longer automatically logs out the previous user when a new user is detected on the device. Logout should now be explicitly called for workspaces enabled with Identity resolution to avoid data corruption.
* **SetUniqueID**: *IdentifyUser* function supports multiple identifiers, which replaces the need of using *SetUniqueID* function for user identification. Note that *SetUniqueID* is marked for removal in the future releases of SDK versions - it is important to use *identifyUser* instead especially if you are using Identity resolution in your workspace.
* **SetAlias**: For workspaces with the Identity resolution feature enabled, MoEngage SDK stores the previous identifier values. When *IdentifyUser* function is used to track the new identifier values, MoEngage SDK detects the change in identifier value and reports accordingly.
* If you call the *IdentifyUser* function without logging out, then the existing logged-in user's ID is updated.
If a network issue prevents the identifier from reaching the MoEngage server, the SDK caches the `identifyUser()` call and retries automatically once the network is available or the next time the app opens.
To enable or disable the SDK and data tracking, refer to [Compliance](/docs/developer-guide/react-native-sdk/compliance/compliance).
**Note**: Before implementing ***identifyUser()*** with multiple identifiers, you must activate the defined identifiers on the MoEngage dashboard. For configuration steps, see [this documentation](https://www.moengage.com/docs/user-guide/data/user-data/unified-identity-identity-resolution#configure-multiple-identifiers).
***Behaviour of Multiple identifyUser() calls***
* When calling ***identifyUser()*** multiple times, the new identifiers are appended to the existing list rather than replacing them. Here's an example of how this works:
```javascript JavaScript theme={null}
import ReactMoE from 'react-native-moengage';
//First call with email
ReactMoE.identifyUser({"u_em":"abc@xyz.com"});
//Later call identifyUser() with mobile Number
ReactMoE.identifyUser({"u_mb":"999999999"});
//Result now the user has both email and mobile identifiers;
```
* If you call ***identifyUser()*** with an identifier key that already exists, the new value will be update the existing one. Here's an example of how this works:
```javascript JavaScript theme={null}
import ReactMoE from 'react-native-moengage';
//First call with initial email
ReactMoE.identifyUser({"u_em":"abc@xyz.com"});
//Later call identifyUser() when User updates their email
ReactMoE.identifyUser({"u_em":"jfk@xyz.com"});
//Result now the user email is updated with the later one;
```
This behaviour allows you to:
* Add new identifiers as they become available
* Update specific identifiers without affecting others
* Build a complete user identity profile over time
Here, `u_em`, `u_mb` are standard user attributes. Please refer to [this section](/docs/developer-guide/react-native-sdk/data-tracking/tracking-user-attributes-and-user-identity) to identify user with more standard user attributes
## Standard and Custom Attributes
**Standard attributes:** These are common user attributes that are pre-defined within the MoEngage dashboard, such as email address and mobile phone number. The table below lists these standard attributes and their corresponding key names
| User Attribute Name | Key name to be used in identifyUser method |
| ------------------------ | ------------------------------------------ |
| ID | uid |
| Email (Standard) | u\_em |
| Gender | u\_gd |
| Birthday | u\_bd |
| Name | u\_n |
| First Name | u\_fn |
| Last Name | u\_ln |
| Mobile Number (Standard) | u\_mb |
**Custom attributes:** These are attributes that you define yourself within the MoEngage dashboard in addition to the standard attributes. Here's an example of how you might work with custom attributes:
```javascript JavaScript theme={null}
import ReactMoE from 'react-native-moengage';
//replace custom_attribute_name with the actual name of your custom user attribute and attributeValue with the actual value you want to assign to the attribute
ReactMoE.identifyUser({ custom_attribute_name: 'attributeValue' });
//you can set two or more identities at the same time
ReactMoE.identifyUser({ cust_attr_1: 'value1', cust_attr_2: 'value2' });
//you can set custom user identity and standard user identity at the same time
ReactMoE.identifyUser({ cust_attr_1: 'value1', cust_attr_2: 'value2', u_em: 'emailValue@emailDomain.com' });
```
For detailed instructions on selecting both custom and standard attributes when configuring multiple identifiers, please refer to [this document](https://www.moengage.com/docs/user-guide/data/user-data/unified-identity-identity-resolution#step-1-select-identifiers).
## Logout User
```javascript JavaScript theme={null}
import ReactMoE from 'react-native-moengage';
ReactMoE.logout();
```
**Critical - Very Important Integration Guideline**
Never use both the login methods - `identifyUser` and `setUserUniqueID()`(method to assign identifier present in versions below 11.2.0) in your project. Use only either one of the methods. Using both the methods can lead to inconsistent user profile creation and merging in your MoEngage account.
### Logout Callback Listener
To receive a callback when logout is complete, register a listener for the `logoutComplete` event:
```javascript JavaScript theme={null}
ReactMoE.setEventListener("logoutComplete", (data) =>
console.log(("Logout completed", data)
// process your logout complete here
);
```
The logout callback listener requires React Native SDK version 12.8.0 or later.
# Tracking User Attributes
Use the following helper methods to set User attributes like Name, Email, Mobile, Gender, etc.
```javascript JavaScript theme={null}
import ReactMoE, {
MoEGeoLocation,
} from "react-native-moengage";
ReactMoE.setUserName("abc");
ReactMoE.setUserFirstName("abc");
ReactMoE.setUserLastName("xyz");
ReactMoE.setUserEmailID("abc@xyz.com");
ReactMoE.setUserContactNumber(1234567890);
ReactMoE.setUserGender("Male"); // OR Female
// Format - ISO-8601 String
ReactMoE.setUserBirthday("1970-01-01T12:00:00Z");
// For Location use MoEGeoLocation instance
ReactMoE.setUserLocation(new MoEGeoLocation(77.3201, -77.3201));
// For array of integers
ReactMoE.setUserAttribute("arrayOfInt",[1,2,3]);
// For array of strings
ReactMoE.setUserAttribute("arrayOfString",['sample1','sample2','sample3']);
```
For setting other User Attributes you can use the generic method ***setUserAttribute(key,value)***
To set custom user attributes, you will have to provide the attribute name as shown below:
```javascript JavaScript theme={null}
import ReactMoE, {
MoEGeoLocation,
} from "react-native-moengage";
ReactMoE.setUserAttribute("attribute name", "attribute value");
// For Time attribute use ISO-8601 format
ReactMoE.setUserAttributeISODateString(
"time attribute name",
new Date().toISOString()
);
// For Location, use MoEGeoLocation instance
ReactMoE.setUserAttributeLocation(
"location attribute name",
new MoEGeoLocation(10.3223, -88.6026)
);
```
You can not use "moe\_" as a prefix while naming events, event attributes, or user attributes. It is a system prefix and using it might result in periodic blacklisting without prior communication.
## Custom Boolean User Attribute
### iOS (optional)
Starting from version 11.x.x of react-native-moengage, the default tracking for the custom boolean attribute will be changed to ***boolean(true/false)*** from ***double(0/1***). To configure this, use ***MoEAnalyticsConfig*** and pass true to track the boolean as double. By default, this is set as **false** to track the boolean as true/false.
Refer to the initialisation code snippet below.
```java JavaScript theme={null}
import ReactMoE from 'react-native-moengage';
import { MoEInitConfig, MoEPushConfig, MoEngageLogConfig, MoEngageLogLevel } from "react-native-moengage";
const moEInitConfig = new MoEInitConfig(
MoEPushConfig.defaultConfig(),
new MoEngageLogConfig(MoEngageLogLevel.DEBUG, isEnabledForReleaseBuild),
new MoEAnalyticsConfig(true)
);
ReactMoE.initialize(YOUR_WORKSPACE_ID, moEInitConfig);
```
Refer to the example code below for tracking the boolean user attribute
```javascript JavaScript theme={null}
import ReactMoE, {
MoEGeoLocation,
} from “react-native-moengage”;
// If MoEAnalyticsConfig is passed as true then `boolean attribute True` will tracked with value 1 else true
ReactMoE.setUserAttribute("boolean attribute True", true);
// If MoEAnalyticsConfig is passed as true then `boolean attribute False` will tracked with value 0 else false
ReactMoE.setUserAttribute("boolean attribute False", false);
```
## Reserved keywords for User Attributes
Below is the list of keys that should not be used when tracking user attributes.
* USER\_ATTRIBUTE\_UNIQUE\_ID
* USER\_ATTRIBUTE\_USER\_EMAIL
* USER\_ATTRIBUTE\_USER\_MOBILE
* USER\_ATTRIBUTE\_USER\_NAME
* USER\_ATTRIBUTE\_USER\_GENDER
* USER\_ATTRIBUTE\_USER\_FIRST\_NAME
* USER\_ATTRIBUTE\_USER\_LAST\_NAME
* USER\_ATTRIBUTE\_USER\_BDAY
* USER\_ATTRIBUTE\_NOTIFICATION\_PREF
* USER\_ATTRIBUTE\_OLD\_ID
* MOE\_TIME\_FORMAT
* MOE\_TIME\_TIMEZONE
* USER\_ATTRIBUTE\_DND\_START\_TIME
* USER\_ATTRIBUTE\_DND\_END\_TIME
* MOE\_GAID
* MOE\_ISLAT
* INSTALL
* UPDATE
* status
* user\_id
* source
You can not use "moe\_" as a prefix while naming events, event attributes, or user attributes. It is a system prefix and using it might result in periodic blacklisting without prior communication.
# React Native SDK Deprecated Versions
Source: https://moengage.com/docs/developer-guide/react-native-sdk/deprecated-versions/deprecated-versions
Check the support status of MoEngage React Native SDK major versions: which are current, which are still supported, and which are deprecated, along with deprecation dates.
Use this page to check the support status of MoEngage React Native SDK major versions. It lists which versions are current, which are still supported, and which are deprecated, along with deprecation dates. This is the source of truth for React Native SDK version status. For how the lifecycle works and what deprecation means for your app, see the [SDK Deprecation Policy](/docs/developer-guide/sdk-lifecycle-and-policies/sdk-deprecation-policy). To set up the React Native SDK, see the [integration guide](/docs/developer-guide/react-native-sdk/overview/getting-started-with-react-native-sdk).
## Version Support Status
**Current** — The latest major version. It receives new features, fixes, and support.
**Supported** — An older major version within its 3-year support window. It continues to receive support.
**Deprecated** — A version outside its support window. It no longer receives fixes or support.
| Major Version | Status | Deprecation Date | Notes |
| --------------------- | ---------- | ---------------- | -------------------------------------------------------------------------------------------------------------- |
| Core 12.x | Current | TBD | Latest major version. Receives new features, fixes, and support. |
| Core 11.x | Supported | TBD | Receives support. |
| Core 10.3.0 and below | Deprecated | August 2027 | Support, debugging, and fixes are no longer available. Data continues to flow. Upgrade to the current version. |
MoEngage supports each major version for 3 years from its initial release; deprecation then takes effect the following August. See the [SDK Deprecation Policy — Support Window](/docs/developer-guide/sdk-lifecycle-and-policies/sdk-deprecation-policy#support-window) for details. This table is updated when MoEngage announces further deprecations. To receive advance notice, subscribe to the SDK newsletter and ensure your workspace administrator email is current.
## Understanding Deprecated Status
A deprecated version continues to send and receive data. MoEngage does not block traffic. After the deprecation date, MoEngage no longer debugs, fixes, or supports issues specific to that version, and the resolution for any issue is to upgrade to the current version. All fixes ship in the current version only and are not available on supported or deprecated versions. For the complete policy, see the [SDK Deprecation Policy](/docs/developer-guide/sdk-lifecycle-and-policies/sdk-deprecation-policy).
## Upgrading to a Supported Version
* Refer to the [React Native SDK release notes](/docs/release-notes/sdks/react-native) for the current major version changes.
* Review the [React-Native](https://github.com/moengage/React-Native) repository for the latest packages.
* For help, contact your MoEngage Customer Success Manager (CSM) or the Support team.
## Frequently Asked Questions
Apps on deprecated versions continue to run and data continues to flow. However, MoEngage no longer provides support, debugging, or fixes for deprecated versions. Upgrading to the current or a supported version using the [React Native SDK release notes](/docs/release-notes/sdks/react-native) ensures you receive continued support.
MoEngage sends advance notice 6 to 12 months before the deprecation date through email to workspace administrators, the quarterly SDK newsletter, in-dashboard notices, and SDK release notes. See the [SDK Deprecation Policy](/docs/developer-guide/sdk-lifecycle-and-policies/sdk-deprecation-policy) for the full notification schedule.
# InApp NATIV
Source: https://moengage.com/docs/developer-guide/react-native-sdk/in-app-messages/inapp-nativ
Configure and display in-app messages in your React Native app using the MoEngage SDK.
In-App Messaging is custom views that you can send to a segment of users to show custom messages or give new offers or take to some specific pages. They can be created from your MoEngage account.
## Installing Android Dependency
### Requirements for displaying images and GIFs in InApp
Starting InApp version **7.0.0,** SDK requires [Glide](https://bumptech.github.io/glide/) to show images and GIFs in the in-apps. You need to add the below dependency in ***android/app/build.gradle*** file.
```gradle Groovy theme={null}
dependencies {
...
implementation("com.github.bumptech.glide:glide:4.9.0")
}
```
# Display In-App
MoEngage refreshes the list of eligible In-App campaigns via a meta API call. During an active session, the list refreshes when the app returns from the background to the foreground (at most once every 15 minutes), and immediately when a new session starts or the app is killed and relaunched.
Call the `showInApp()` wherever InApp has to be shown in the app as shown below :
```javascript JavaScript theme={null}
import ReactMoE from 'react-native-moengage'
ReactMoE.showInApp()
```
# Display Nudges
Starting with ***react-native-moengage*** version **9**\*\*.0.0,\*\*MoEngage InApp SDK supports displaying Non-Intrusive nudges.
To show a Nudge InApp Campaign call `showNudge()`
```javascript JavaScript theme={null}
import { ReactMoE, MoEngageNudgePosition } from "react-native-moengage";
ReactMoE.showNudge() // Display Nudge on any available position
ReactMoE.showNudge(MoEngageNudgePosition._NUDGE_POSITION) // Display Nudge on the specific position
```
# InApp/Nudge Redirection default behavior
On clicking an Inapp or Nudge, MoEngage SDKs will handle **only rich landing navigation** redirection.
For the screen name and deep link redirection, your app code should redirect the user to the right screen or deep link. To handle the screen name and deep link redirection, you must implement inapp click callback methods. MoEngage SDK will just pass the inapp payload to this call back code. Implementation steps are mentioned in the InApp callback section of the Integration.
# Self-Handled InApps
Self-handled In Apps are messages that the SDK delivers, but displaying them has to be handled by the app.
## Single Self-Handled InApps
To get self-handled In-App call the below method.
```javascript JavaScript theme={null}
import ReactMoE from 'react-native-moengage'
ReactMoE.getSelfHandledInApp();
```
The payload for self-handled in-app is returned via a callback. Register a callback as shown below.
```javascript JavaScript theme={null}
import ReactMoE from 'react-native-moengage'/// selfHandledCampaignData is of type MoESelfHandledCampaignData
ReactMoE.setEventListener("inAppCampaignSelfHandled", (selfHandledPayload) => {
if (selfHandledCampaignData && Object.keys(selfHandledPayload).length != 0) {
console.log("inAppCampaignSelfHandled", selfHandledCampaignData);
}
});
```
## Multiple Self-Handled InApps
* This feature is supported from version ***11.1.0*** of the plugin.
Fetch Multiple Self Handled Campaigns using *getSelfHandledInApps()*.
```javascript JavaScript theme={null}
import ReactMoE from 'react-native-moengage'
/// selfHandledCampaignsData is of type MoESelfHandledCampaignsData, which contains
/// a list of MoESelfHandledCampaignData data objects
var selfHandledCampaignsData = await ReactMoE.getSelfHandledInApps();
```
### Tracking Statistics for Multiple Self-Handled In-Apps
The *getSelfHandledInApps()* method returns [*MoESelfHandledCampaignsData*](/docs/developer-guide/react-native-sdk/in-app-messages/inapp-nativ)\*\*,\*\*which contains a list of [*MoESelfHandledCampaignData*](/docs/developer-guide/react-native-sdk/in-app-messages/inapp-nativ) objects. The statistics for each [*MoESelfHandledCampaignData*](/docs/developer-guide/react-native-sdk/in-app-messages/inapp-nativ) object must be tracked individually below APIs.
### Fetching Contextual Multiple Self-Handled InApps
To fetch contextual multiple self-handled inapps, set the inapp contexts using *setCurrentContext(*)before calling \*getSelfHandledInApps().\*This will return a list of contextual and non-contextual inapps(in the order of campaign priority set at the time of campaign creation).
# Tracking Statistics
Since display, click, and dismiss for Self-Handled InApp are controlled by the application we need you to notify the SDK whenever the In-App is *Shown*, *Clicked*, or *Dismissed*. Below are the methods you need to call to notify the SDK. The campaign object which is an instance of the *MoESelfHandledCampaignData* object provided to the application in the callback for self-handled in-app should be passed in as a parameter to the below APIs.
```javascript JavaScript theme={null}
import ReactMoE from 'react-native-moengage'
ReactMoE.selfHandledShown(selfHandledCampaignData);
ReactMoE.selfHandledClicked(selfHandledCampaignData);
ReactMoE.selfHandledDismissed(selfHandledCampaignData);
```
# InApp Callbacks
The callbacks must be registered before inapps are displayed either via show methods or triggered events. And make sure you are calling `initialize()` the method of the plugin after you set up these callbacks. Refer [doc](/docs/developer-guide/react-native-sdk/sdk-integration/react-native/sdk-initialization/manual-initialization/framework-initialization) for more info.
We provide callbacks whenever an InApp campaign is shown, dismissed, or clicked you can register for the same as shown below.
```javascript JavaScript theme={null}
import ReactMoE from 'react-native-moengage'
///inAppInfo is of type MoEInAppData
ReactMoE.setEventListener("inAppCampaignShown", (inAppInfo) =>
console.log("inAppCampaignShown", inAppInfo)
);
///inAppInfo is of type MoEClickData
ReactMoE.setEventListener("inAppCampaignClicked", (inAppInfo) =>
console.log("inAppCampaignClicked", inAppInfo)
);
///inAppInfo is of type MoEInAppData
ReactMoE.setEventListener("inAppCampaignDismissed", (inAppInfo) =>
console.log("inAppCampaignDismissed", inAppInfo)
);
///inAppInfo is of type MoEClickData
ReactMoE.setEventListener("inAppCampaignCustomAction", (inAppInfo) =>
console.log("inAppCampaignCustomAction", inAppInfo)
);
```
| Event Type | Event Name |
| -------------------------------- | ------------------------- |
| InApp Shown | inAppCampaignShown |
| InApp Clicked | inAppCampaignClicked |
| InApp Dismissed | inAppCampaignDismissed |
| InApp Clicked with Custom Action | inAppCampaignCustomAction |
# Contextual InApp
You can restrict the in-apps based on the user's context in the application apart from restricting InApp campaigns on a specific screen/activity. To set the user's context in the application use *setCurrentContext()* API as shown below.
## Set Context
Call the below method to set the context, before calling *showInApp().*
```javascript JavaScript theme={null}
import ReactMoE from 'react-native-moengage'// replace array elements with actual values.
ReactMoE.setCurrentContext(['c1', 'c2', 'ce'])
```
## Reset Context
Once the user is moving out of the context use the *resetCurrentContext()* API to reset/clear the existing context.
```javascript JavaScript theme={null}
import ReactMoE from 'react-native-moengage'
ReactMoE.resetCurrentContext();
```
For more information on Contextual InApp, refer to the video tutorial available in [Troubleshooting and FAQs](/docs/developer-guide/android-sdk/troubleshooting-and-faqs/troubleshooting-and-faqs#how-to-use-contextual-inapps-in-react-nativ).
# Payload Structure
```typescript TypeScript theme={null}
class MoEInAppData {
/// account information
accountMeta: MoEAccountMeta;
/// Platform on which callback is received
platform: MoEPlatform;
///InApp data
campaignData: MoECampaignData;
}
class MoECampaignData {
/// Unique Campaign Identifier
campaignId: string;
/// Campaign Name
campaignName: string;
/// additional information associated with Campaign
context: MoECampaignContext;
}
class MoESelfHandledCampaignData {
///InApp data
campaignData:MoECampaignData;
/// account information
accountMeta: MoEAccountMeta;
///Platform on which callback is received
platform: MoEPlatform;
///SelfHandled data
campaign: MoESelfHandledCampaign
}
class MoESelfHandledCampaign {
///Campaign Content provided while creating campaign
payload: string;
///auto dismiss interval in seconds
dismissInterval: Number;
/// DisplayRules for Campaign
displayRules: MoEInAppRules;
}
class MoEInAppRules {
/// Screenname for which InApp was configured to be shown.
/// @deprecated Use the 'screenNames' property instead.
screenName: string | null;
/// contexts for which InApp was configured to be shown.
contexts: Array;
/// Screennames for which InApp was configured to be shown.
/// @since 12.0.0
screenNames: Array;
}
class MoEClickData {
///account information
accountMeta: MoEAccountMeta;
///Platform on which callback is received
platform: MoEPlatform;
///InApp Data
campaignData: MoECampaignData;
///InApp Click action
action: MoEAction;
}
class MoEInAppCustomAction extends MoEAction{
/// Key-Value pairs configured with action
keyValuePair: Map;
///Click action type
actionType: MoEActionType;
}
class MoEInAppNavigation extends MoEAction {
///navigation action type screen/deeplink
navigationType: MoENavigationType;
// ScreenName OR deeplink URL based on navigation type
navigationUrl: String;
// Key-Value pairs configured with action
keyValuePair?: Map;
///Click action type
actionType :MoEActionType;
// Model for Multiple SelfHandled Data
class MoESelfHandledCampaignsData {
///account information
accountMeta: MoEAccountMeta;
///List of SelfHandled data
campaigns: Array
}
```
# Handling Orientation Change
This is only for the Android platform
Starting SDK version `7.3.0`, in-apps are supported in both portrait and landscape modes.\
SDK has to be notified when the device orientation changes for SDK to handle in-app display.
## Add the API call in the Android native part of your app
Notify the SDK when `onConfigurationChanged()` API callback is received in your App's Activity class.
```java Java theme={null}
import android.content.res.Configuration;
import androidx.annotation.NonNull;
import com.facebook.react.ReactActivity;
import com.moengage.react.MoEReactHelper;
public class MainActivity extends ReactActivity {
/**
* Returns the name of the main component registered from JavaScript. This is used to schedule
* rendering of the component.
*/
@Override
protected String getMainComponentName() {
return "SampleApp";
}
@Override public void onConfigurationChanged(@NonNull Configuration newConfig) {
super.onConfigurationChanged(newConfig);
MoEReactHelper.getInstance().onConfigurationChanged();
}
}
```
# Display status bar
To display the status bar, use the following code snippet during Android SDK initialization:
```java Java wrap theme={null}
.configureInApps(new InAppConfig(false))
```
# Getting Started with React Native SDK
Source: https://moengage.com/docs/developer-guide/react-native-sdk/overview/getting-started-with-react-native-sdk
Get started with the MoEngage React Native SDK for push notifications, in-app messages, and tracking.
# Overview
MoEngage’s React Native SDK helps you integrate MoEngage into iOS and Android applications built with React Native framework. It allows you to work with push notifications, in-app messages, cards, user attributes, events, and much more.
To see the sample code, take a look at the [GitHub repository](https://github.com/moengage/React-Native). This article describes the steps to implement MoEngage features on React Native.
You can now get notified whenever MoEngage releases a new version of the React Native SDK. For more information, refer to [Subscribe to MoEngage SDK Releases](/docs/release-notes/sdks/react-native).
Review the [SDK Deprecation Policy](/docs/developer-guide/sdk-lifecycle-and-policies/sdk-deprecation-policy) for supported SDK versions and deprecation timelines before integrating.
# SDK Installation and Initialization
There are two methods for managing the MoEngage SDK: using the MoEngage Expo Plugin or configuring the native layers directly.
The Expo Plugin allows you to configure many SDK features without writing native code. For more information, see the [MoEngage Expo Plugin documentation](/docs/developer-guide/react-native-sdk/sdk-integration/expo/installation). \
To configure the SDK by managing the native layers, follow the steps below:
**Step 1: Installation**
To add MoEngage's React Native SDK to your application, refer to [Installation Methods](/docs/developer-guide/react-native-sdk/sdk-integration/react-native/sdk-installation/framework-dependency).
**Step 2: Complete Native Android Setup**
* Android native setup guidelines to complete the installation are described in this [article](/docs/developer-guide/react-native-sdk/sdk-integration/react-native/sdk-installation/android).
* There are no additional steps required for iOS. Move on to the initialization (Step 3).
**Step 3: Framework Initialization**\
Initialize an instance of the MoEngage plugin by calling the method described in this [article](/docs/developer-guide/react-native-sdk/sdk-integration/react-native/sdk-initialization/manual-initialization/framework-initialization).
**Step 4: Platform Initialization**\
The platform-specific steps to initialize the SDK and set up the data center are described in the following articles:
* [Android](/docs/developer-guide/react-native-sdk/sdk-integration/react-native/sdk-initialization/manual-initialization/android)
* [iOS](/docs/developer-guide/react-native-sdk/sdk-integration/react-native/sdk-initialization/manual-initialization/ios)
# Data Tracking
Data tracking allows apps to monitor and analyze user behavior to optimize engagement strategies. It involves tracking various user actions such as login, logout, and event tracking in a way that avoids data corruption. Use the following methods to implement data tracking.
* **Install/Update Differentiation** - To track fresh installs and updates separately, refer to the methods in this [article](/docs/developer-guide/react-native-sdk/data-tracking/install-update).
* **Tracking Login, Logout, and Setting Unique ID** - To avoid data corruption, it is crucial to follow the steps outlined in the following articles when handling user login and logout.
* [Tracking Login and set user ID](/docs/developer-guide/react-native-sdk/data-tracking/setting-unique-id-for-sdk-versions-below-1120#login-user)
* [Tracking Logout](/docs/developer-guide/react-native-sdk/data-tracking/setting-unique-id-for-sdk-versions-below-1120#logout-user)
* [Updating User Attribute Unique ID](/docs/developer-guide/react-native-sdk/data-tracking/setting-unique-id-for-sdk-versions-below-1120) It is essential to have a unique ID for each of your app's users, which can be passed onto MoEngage SDK using setUserUniqueID(). This unique ID helps to correctly identify a user across multiple installs and platforms to provide a unified view. Once a user logs out of the app, it's critical to call logout() to initiate the creation of a new user. This step is necessary to ensure that any subsequent activities performed by the new user are not wrongly attributed to the previously logged-in user, which could distort user data.
* **Tracking user attributes** - To set custom attributes available in the user profile, refer to the methods in [this article](/docs/developer-guide/react-native-sdk/data-tracking/setting-unique-id-for-sdk-versions-below-1120).
* **Tracking Events** - Tracking events is how you record user actions, along with any properties that describe the action. To track custom events, refer to the methods in [this article](/docs/developer-guide/react-native-sdk/data-tracking/tracking-events).
* **Enable Advertising Identifier Tracking (Android only)** - MoEngage SDK uses a Device ID (persistent device identifier) to uniquely identify the user to deliver personalized content and associates this to AAID if allowed by the app. This allows accurate identification of reachable devices for sending push notifications and tracking re-installs for users over time. To enable tracking of the AAID after obtaining the user’s consent, refer to the methods in [this article](/docs/developer-guide/react-native-sdk/data-tracking/enable-advertising-identifier-tracking).
# Push Notifications
Push campaigns target users through notifications for your app or website. Depending on the desired capability, follow the integration steps listed below to integrate push notifications.
## Basic Setup - Android
Follow the basic setup outlined in this section to enable push notifications on an Android device using MoEngage.
* **FCM Setup on MoEngage Dashboard -** FCM Authentication is the method to enable sending push notifications to your app installed on Android devices. Use the methods mentioned in [this article](/docs/developer-guide/android-sdk/push/basic/fcm-authentication) to authenticate MoEngage to access Firebase services.
* **Adding metadata for push notification -** Set the small icon and large icon drawable and other options to handle push notifications using the methods available in [this article](/docs/developer-guide/components-for-sdk/push-notification/android-push-configuration-for-hybrid-applications#adding-metadata-for-push-notification).
* **Android Notification Runtime Permissions** - When the application is running on Android 13 to show notifications to the user, applications would need to request the user for notification permission. Refer to the methods available in [this article](/docs/developer-guide/react-native-sdk/push/basic/android-notification-runtime-permissions) to handle permission requests.
* **Push Registration and Receiving** - To use Push Notification in your React Native application, you must configure Firebase. Configuring Firebase steps will depend on how you want to integrate it. MoEngage recommends leaving the push handling to MoEngage SDK, as the best practices are properly integrated. You can also handle the push at your app level. In any case, look at the following section that applies to you and finish the integration steps.
**Add messaging service**\
You must add the messaging service to the Manifest file for MoEngage SDK to show the notifications. Refer to this document [here](/docs/developer-guide/android-sdk/push/basic/push-token-registration-and-display#push-token-registration-and-display-by-moengage-sdk).
**Callback on token registration (optional)**\
To get a callback whenever a new token is registered or refreshed, refer to the method [here](/docs/developer-guide/android-sdk/push/basic/push-token-registration-and-display#token-callback-access-to-push-token-optional).
**Notification Clicked Callback**
To receive a callback whenever a push is clicked and for custom handling redirection, use the method in [this article](/docs/developer-guide/android-sdk/push/advanced/callbacks-and-customisation#notification-clicked-callback).
**How to opt out of MoEngage Registration?**\
The MoEngage SDK attempts to register for a push token; since your application handles push, you need to opt out of SDK's token registration using the method in [this article](/docs/developer-guide/android-sdk/push/basic/push-token-registration-and-display#how-to-opt-out-of-moengage-push-token-registration).
**Pass the Push Token To MoEngage SDK** - After receiving the push token from FCM, use the method in [this article](/docs/developer-guide/android-sdk/push/basic/push-token-registration-and-display) to pass the Push Token to the MoEngage SDK to set it in the MoEngage platform.
**Passing the Push payload to the MoEngage SDK** - After receiving the push payload on the app, use the method in [this article](/docs/developer-guide/android-sdk/push/basic/push-token-registration-and-display#passing-the-push-payload-to-the-moengage-sdk) to send out push notifications to the device.
MoEngage recommends using the Android native APIs to pass the push payload to the MoEngage SDK instead of the React-Native/Javascript APIs. React-Native Engine might not get initialized if the application is killed or if the notification is not sent at a high priority.
* [Pass the Push Token To MoEngage SDK](/docs/developer-guide/react-native-sdk/push/basic/android-push-configuration#passing-push-token)
* [Pass the Push payload to the MoEngage SDK](/docs/developer-guide/react-native-sdk/push/basic/android-push-configuration#passing-push-payload)
* [Callbacks and customizations](/docs/developer-guide/react-native-sdk/push/basic/android-push-configuration#customizing-push-notification)
**Notification Clicked Callback -** MoEngage's React Native plugin optionally provides a callback on push clicks with the method in [this article](/docs/developer-guide/react-native-sdk/push/basic/push-callback).
## Basic Setup - iOS
Follow the basic setup outlined in this section to enable push notifications on an iOS device using MoEngage.
* **APNS Setup on MoEngage dashboard**\
APNS Authentication is the method to enable sending push notifications to your app installed on Android devices. You can use any of these options to set up APNS on the MoEngage dashboard.
* [APNS Authentication Key (recommended)](/docs/developer-guide/ios-sdk/push/basic/apns-authentication-key)
* [APNS Certificate/PEM file](/docs/developer-guide/ios-sdk/push/basic/apns-certificate-pem-file-legacy)
* **App target implementation** - Make changes to your app target to enable notifications by following the steps mentioned in [this article](/docs/developer-guide/ios-sdk/push/basic/ios-push-integration-tutorial).
* **Provide the App Group ID to SDK**- Pass the App Group ID to MoEngage SDK using the method in [this article](/docs/developer-guide/ios-sdk/push/basic/ios-push-integration-tutorial).
* **Push Registration and Receiving**
In MoEngage SDK, we have swizzled the AppDelegate Class to get all the callbacks related to Push Notifications, and we have also applied the method swizzling for UserNotificationCenter delegate methods. This is to ease the integration of the SDK.
**Registering for Push notification**\
Follow the steps in [this article](/docs/developer-guide/ios-sdk/push/basic/ios-push-integration-tutorial#choose-a-notification-registration-method) to initiate registration.
**Callback methods on receiving Push Notification**\
With Swizzling enabled, no additional configuration is required.
In case you do not prefer to use swizzling, you can disable the same by adding the flag MoEngageAppDelegateProxyEnabled in the app’s Info.plist file and setting it to Boolean value NO, and follow the steps below.
**Registering for Push notification -** Follow the steps in [this article](https://moengage-sdk-docs.mintlify.app/developer-guide/ios-sdk/push/basic/ios-push-integration-tutorial#choose-a-notification-registration-method) to initiate registration and the steps in [this section](/docs/developer-guide/ios-sdk/push/basic/ios-push-integration-tutorial#app-delegate-method-swizzling) to call the respective MoEngage SDK methods for registration callbacks.
**Callback methods on receiving Push Notification -** With Swizzling disabled, include calls to MoEngage SDK methods on receiving notification callbacks, as described in [this article](/docs/developer-guide/ios-sdk/push/basic/ios-push-integration-tutorial#app-delegate-method-swizzling).
* **Disable Badge Reset**\
By default, the SDK sets the notification badge count to 0 on every app launch, and this also clears the notifications in the device notification center. If you want to keep the notifications even after the App Launch, disable badge reset by calling the method in [this section](https://moengage-sdk-docs.mintlify.app/developer-guide/ios-sdk/push/basic/ios-push-integration-tutorial#configure-push-notification-badge-behavior).
* **Custom Sound for Notification**\
To optionally set a custom tone for notifications of your app, you can follow the method in [this section](/docs/developer-guide/ios-sdk/push/advanced/custom-notification-handling#custom-sound-for-notification).
* **Notification Service Extension Target Implementation**\
The notification service extension allows MoEngage SDK to customize the content of a notification before the system delivers it to the user. This gives you the capability to add media in notifications, support inbox, update badge count on notifications delivered, and track notification impressions. Follow the steps in [this article](/docs/developer-guide/ios-sdk/push/basic/ios-push-integration-tutorial#step-1-implement-notification-service-extension-nse) to set up the notification service extension.
* **Notification Actions**\
Actionable notifications let you add custom action buttons to the standard iOS push notifications. Follow the steps mentioned in [this article](/docs/developer-guide/ios-sdk/push/basic/actionable-notifications) to add custom actions to your notifications and track the actions performed on notifications
This completes your basic setup for push notifications in React Native.
## Push Templates
Push templates enable you to craft beautiful notifications within minutes without any coding. For information on how to create campaigns with templates in the dashboard, refer to [this article](https://www.moengage.com/docs/developer-guide/ios-sdk/push/optional/push-templates).
To enable push templates, please follow the platform-specific documentation
* [Android](/docs/developer-guide/android-sdk/push/optional/push-templates#sdk-installation)
* [iOS](/docs/developer-guide/ios-sdk/push/optional/push-templates#1-create-a-notification-content-extension)
## Push Amp+ (Android only and Optional)
Around 25-30% of notifications are not delivered due to issues with original equipment manufacturer (OEM) devices. To combat this problem and improve retention rates, MoEngage developed Push Amplification+ to reach customers who may not have received notifications. MoEngage has also partnered with OEMs to address these issues and ensure that notifications are reliably delivered. To minimize any additional burden on your application, we have developed individual software development kits (SDKs) for each OEM. You can choose and integrate the relevant SDK based on your application's specific needs and device share. Refer to the documentation for each OEM-specific service and integrate the appropriate ones into your application for optimal push notification delivery.
### Supported Integrations
* [HMS Push Kit](/docs/developer-guide/android-sdk/push/optional/push-amp-plus/configuring-hms-push-kit)
## Push Amplification (Android only and Optional)
Push Amplification works as a fallback mechanism when Firebase Cloud Messaging (FCM) fails to deliver Push Notifications. Follow the method here to set up [Push Amplification](/docs/developer-guide/android-sdk/push/optional/push-amplification).
## Notification Center
The Notification Center shows your push notification history, allowing you to provide an option for the end-user to scroll back and see what they have missed. MoEngage provides out-of-box inbox support with a fully customizable default UI and also provides an option to build your own Notification Center. For more information, refer to [Notification Center](/docs/developer-guide/react-native-sdk/push/optional/notification-triggered).
## Location Triggered Notifications (Optional)
Location-triggered notifications allow you to send messages to your audience that are triggered on the user’s entry, exit, and dwell in defined Geo Fences. Follow the method in [this article](/docs/developer-guide/react-native-sdk/push/optional/location-triggered) to set up location triggers.
## Device triggered notifications (Optional)
Device-triggered notifications allow you to send messages to your audience that are triggered locally based on any activity on a device. Offline delivery of messages is supported as well.
To enable device-triggered notifications, use the following platform-specific articles:
* [Android](/docs/developer-guide/android-sdk/push/optional/device-triggered)
* [iOS](/docs/developer-guide/ios-sdk/push/optional/real-time-triggers)
## Advanced Use Cases in Android
For advanced use cases, the following options are available:
* **Non-MoEngage Payload** - To get an optional callback in case a push payload is received for any other server apart from the MoEngage Platform, refer to the method [here](/docs/developer-guide/android-sdk/push/basic/push-token-registration-and-display#push-display-for-non-moengage-payloads-optional).
* **Callbacks and customizations** - The MoEngage SDK allows the client application to optionally customize the notification display and extend/customize the behavior of the notification. Refer to the methods mentioned [here](/docs/developer-guide/android-sdk/push/advanced/callbacks-and-customisation) to access the features, such as:
* Control whether a notification is shown to the user or not
* Notification Received Callback
* Notification Clicked Callback
* Notification Cleared Callback
* Custom Action on Action Button Click
* **Push Display Handled by Application(Android)** - When the application needs to handle the push display on the client side, you can track notification impressions and cases using the methods described in [this article](/docs/developer-guide/android-sdk/push/advanced/push-display-handled-by-application).
# In-App
MoEngage In-App Campaigns target users by showing a message while the user is using your app. They are effective in providing contextual information and help to cross-sell/up-sell on desired screens of your app or/and on desired actions performed by the user in your app.
Basic Setup - To install In-app notifications in React Native, use the following platform-specific methods:
* [Android](/docs/developer-guide/react-native-sdk/in-app-messages/inapp-nativ#installing-android-dependency)
* iOS (installation is not required for iOS)
## Displaying In-App Messages
You can either show In-app messages using MoEngage’s out-of-the-box UI, or you can use Self-handled In-apps to build the UI of the application using the payload from MoEngage.
**Show In-app**\
Call the method [here](https://moengage-sdk-docs.mintlify.app/developer-guide/react-native-sdk/in-app-messages/inapp-nativ#installing-android-dependency) to show an inApp campaign on a screen. In-app pop-ups will only show up where showInApp() method is called.
**Handling Orientation Change**\
In-apps are supported in both portrait and landscape modes. SDK has to be notified when the device orientation changes for SDK to handle in-app display. To handle orientation change, refer to [this article](developer-guide/react-native-sdk/in-app-messages/inapp-nativ#handling-orientation-change).
**Self-handled In-apps**\
Self-handled In Apps are messages that are delivered by the SDK to the application, and the application builds the UI using the delivered payload by the SDK. To get the self-handled in-app, refer to [this article](https://moengage-sdk-docs.mintlify.app/developer-guide/react-native-sdk/in-app-messages/inapp-nativ#display-in-app).
* [Getting self-handled campaigns](/docs/developer-guide/react-native-sdk/in-app-messages/inapp-nativ#self-handled-inapps)
* [Tracking Statistics for Self-Handled In-Apps](/docs/developer-guide/react-native-sdk/in-app-messages/inapp-nativ#tracking-statistics)
## InApp Callbacks
Optionally, we provide callbacks for in-app shown, in-app clicked, in-app dismissed, and self-handled in-app available events. You can register for the callbacks using the methods in [this article.](/docs/developer-guide/react-native-sdk/in-app-messages/inapp-nativ)
# Cards
MoEngage Cards campaigns help you interact with your users with persistent and non-intrusive messages in your customer's journey. Self-handled cards are message payloads that are delivered by the SDK to the application, and the application builds the UI using the delivered payload.
Refer to [this article](/docs/developer-guide/react-native-sdk/cards/self-handled-cards) to implement self-handled cards on React Native.
# Personalize Data Payload
Source: https://moengage.com/docs/developer-guide/react-native-sdk/personalize/personalize-data-payload
Review the data models and payload structure returned by the MoEngage React Native Personalize SDK.
Review the data models and payload structure returned by the MoEngage React Native Personalize SDK.
## DataSource
```typescript TypeScript theme={null}
enum DataSource {
/** Returned from local cache. */
CACHE,
/** Fetched from the MoEngage backend. */
NETWORK,
}
```
## ExperienceStatus
```typescript TypeScript theme={null}
enum ExperienceStatus {
/** Currently running. */
ACTIVE,
/** Manually paused on the dashboard. */
PAUSED,
/** Scheduled to start in the future. */
SCHEDULED,
}
```
## ExperienceFailureReason
```typescript TypeScript theme={null}
type ExperienceFailureReason =
| "USER_IN_CAMPAIGN_CONTROL_GROUP"
| "USER_IN_GLOBAL_CONTROL_GROUP"
| "USER_NOT_IN_SEGMENT"
| "INVALID_EXPERIENCE_KEY"
| "MAX_LIMIT_BREACHED"
| "EXPERIENCE_NOT_ACTIVE"
| "EXPERIENCE_EXPIRED"
| "PERSONALIZATION_FAILED";
```
## ExperienceCampaign
```typescript TypeScript theme={null}
class ExperienceCampaign {
/** The unique identifier for the experience. */
experienceKey: string;
/** The JSON payload containing personalization data. */
payload: Record;
/** Context for tracking (passed to impression/click events). */
experienceContext: Record;
/** Whether data came from cache or network. */
source: DataSource;
}
```
## ExperienceCampaignFailure
```typescript TypeScript theme={null}
class ExperienceCampaignFailure {
/** The failure reason code. */
reason: ExperienceFailureReason;
/** Experience keys affected by this failure. */
experienceKeys: string[];
}
```
## ExperienceCampaignsResult
```typescript TypeScript theme={null}
class ExperienceCampaignsResult {
/** Successfully fetched experience campaigns. */
experiences: ExperienceCampaign[];
/** Per-key failures (business logic errors from server). */
failures: ExperienceCampaignFailure[];
}
```
## ExperienceCampaignMeta
```typescript TypeScript theme={null}
class ExperienceCampaignMeta {
/** The unique identifier for the experience. */
experienceKey: string;
/** The display name of the experience. */
experienceName: string;
/** The current status of the experience. */
status: ExperienceStatus;
}
```
## ExperienceCampaignsMetadata
```typescript TypeScript theme={null}
class ExperienceCampaignsMetadata {
/** Whether data came from cache or network. */
source: DataSource;
/** List of experience metadata entries. */
experiences: ExperienceCampaignMeta[];
}
```
# Personalize SDK
Source: https://moengage.com/docs/developer-guide/react-native-sdk/personalize/personalize-sdk
Learn how to integrate the MoEngage Personalize SDK for React Native to fetch personalized content, handle offering campaigns, and track performance.
# Overview
The MoEngage Personalize SDK provides a secure framework for delivering personalized campaigns. It simplifies integration by handling user identity and authentication internally, eliminating the need to manage API secrets or manual HTTPS calls.
**Prerequisite**
Before you can fetch personalized experiences, ensure you have initialized the core MoEngage SDK within your application. For more information, refer to [React Native SDK Initialization](https://www.moengage.com/docs/developer-guide/react-native-sdk/sdk-integration/react-native/sdk-initialization/manual-initialization/framework-initialization).
# How It All Fits Together
Before writing any code, it is helpful to understand the three moving parts of the personalization workflow:
1. **Dashboard Configuration:** A marketer creates an Experience Campaign in the MoEngage dashboard and assigns it a unique `experienceKey` (e.g., `home_banner`). They configure the specific JSON payload to be returned for different user segments.
2. **The Meta Call :** Your application calls `fetchExperiencesMeta` to discover which experience keys are active and available for the current user.
3. **The Fetch Call :** Your application calls `fetchExperience` or `fetchExperiences` with a specific key to retrieve the actual payload. The SDK uses the metadata gathered in Step 2 to accurately resolve and return this request.
The SDK returns **raw** JSON only. As the developer, you are responsible for parsing this payload and building the corresponding UI in your application.
# Integrate MoEngage Personalization
To add MoEngage's Personalize SDK to your project, use the command below.
```shellscript Shell theme={null}
npm install react-native-moengage-personalize
```
# Implementation Workflow
The Personalize helper for React Native is designed to simplify the retrieval and interaction with dynamic, personalized content. Below is a breakdown of the workflow and code placeholders.
## 1. Fetch Meta Experience
Before fetching any specific payload or experience, you must invoke the metadata call. Prefetching the metadata helps you optimize the experience fetch. On the app side, you can use the metadata to identify the right personalized content for the current UI state and fetch only the relevant content instead of all the content in the application.
```javascript React Native wrap theme={null}
import ReactMoEngagePersonalize, { ExperienceStatus } from "react-native-moengage-personalize";
const personalize = new ReactMoEngagePersonalize("YOUR_WORKSPACE_ID");
// Create an array with all possible statuses
const status = [
ExperienceStatus.ACTIVE,
];
personalize.fetchExperiencesMeta(status)
.then(metadata => // add logic here to process the metadata)
.catch(error => // add logic for fallback/error handling);
```
The returned object is a Promise that holds the [`ExperienceCampaignsMetadata` ](/docs/developer-guide/react-native-sdk/personalize/personalize-data-payload)object, containing all necessary metadata for campaign execution. The rejection contains the [`FailureReason`](/docs/developer-guide/react-native-sdk/personalize/personalize-data-payload) and an optional message to identify the specific reason for the request's failure.
## 2. Fetch Personalized Content
Once metadata is fetched, you can retrieve the actual personalized payloads. You can fetch a single experience or multiple experiences simultaneously, with full support for contextual targeting.
EXPERIENCE\_KEY is the unique key that is used in the experience campaign while creating the campaign on the MoEngage dashboard. You can find this key in the `ExperienceCampaignMeta` object of the `ExperienceCampaignsMetadata` returned on successful metadata fetch.
```javascript React Native wrap theme={null}
import ReactMoEngagePersonalize, { ExperienceStatus } from "react-native-moengage-personalize";
const personalize = new ReactMoEngagePersonalize("YOUR_WORKSPACE_ID");
// additional attributes you want to pass for the experience.
const attributes = {};
```
### Fetch Single Experience
* **Single Experience**: Retrieve a single experience using a single experience key.
```javascript React Native wrap theme={null}
personalize.fetchExperience(, attributes)
.then(result => console.log("Single Experience:", result))
.catch(error => console.error(error));
```
### Fetch Multiple Experiences
* **Bulk Experiences**: Retrieve multiple experiences by passing an array of experience keys.
```javascript React Native wrap theme={null}
const experienceKeys = [, ];
personalize.fetchExperiences(experienceKeys, attributes)
.then(result => console.log("Multiple Experiences:", result))
.catch(error => console.error(error));
```
The returned object is a Promise that holds the [`ExperienceCampaignsResult`](/docs/developer-guide/react-native-sdk/personalize/personalize-data-payload) object containing all necessary metadata for campaign execution. The rejection contains the [`FailureReason`](/docs/developer-guide/react-native-sdk/personalize/personalize-data-payload) and an optional message to identify the specific reason for the request's failure.
Use Cases:
**Contextual Targeting**: Pass an object of attributes (e.g., `{"current_page": "home", "cart_value": "500"}`) during the fetch. This enables real-time, state-dependent content delivery (e.g., showing a "Free Shipping" banner if the cart value meets a threshold).
## 3. Notify SDK on Showing the Experience (Track Impressions)
An *impression* is a telemetry event that notifies the MoEngage SDK that a personalized campaign payload has successfully rendered on the UI and is visible to the user.
To accurately track campaign performance, you must invoke the following methods the moment your application displays the personalized content on the screen.
### 3a. Notify SDK for Experience Campaigns
Call the `experiencesShown()` method when the UI element containing the experience renders.
```javascript React Native wrap theme={null}
import ReactMoEngagePersonalize, { ExperienceCampaign } from "react-native-moengage-personalize";
const personalize = new ReactMoEngagePersonalize("YOUR_WORKSPACE_ID");
// campaigns is an array of ExperienceCampaign objects received from fetchExperiences
// Pass the exact objects or payloads previously received from the fetchExperiences() method.
const campaigns: ExperienceCampaign[] = [/* campaign objects */];
personalize.experiencesShown(campaigns);
```
### 3b. Notify SDK for Offering Campaigns
[Offerings](/docs/user-guide/decisioning/offer-decisioning/create-offerings) are a distinct subtype of personalized content configured by marketers on the MoEngage dashboard (such as dynamic product recommendations, catalogs, or unique coupon codes). Before tracking, it is important to understand how to handle offering payloads within your application:
* Fetching an Offering: You retrieve an offering payload by calling the `fetchExperience()` or `fetchExperiences()` methods. The SDK processes the metadata and returns the payload within the `ExperienceCampaignsResult` object.
* Identifying an Offering: You can identify an offering by inspecting the structure of the returned JSON. An offering payload consists of structured data nested under a specific custom offering key.
* Building the Offering UI: The SDK only returns this offering data as raw JSON. As the developer, you must write the logic to parse this JSON payload and build the corresponding visual UI components on the screen.
Once the offering UI is built and successfully rendered to the user, the SDK provides dedicated tracking functions that accept offering-specific attributes.
```javascript React Native wrap theme={null}
import ReactMoEngagePersonalize from "react-native-moengage-personalize";
const personalize = new ReactMoEngagePersonalize("YOUR_WORKSPACE_ID");
// object containing offering details for a specific offering campaign
// Pass the exact objects or payloads previously received from the fetchExperiences() method.
const offeringPayloads = [
];
personalize.offeringShown(offeringPayloads);
```
## 4. Track Clicks
### 4a. Track Clicks for Experience Campaigns
[Offerings](/docs/user-guide/decisioning/offer-decisioning/create-offerings) are a distinct subtype of personalized content configured by marketers on the MoEngage dashboard (such as dynamic product recommendations, catalogs, or unique coupon codes). Before tracking, it is important to understand how to handle offering payloads within your application:
* Fetching an Offering: You retrieve an offering payload by calling the `fetchExperience()` or `fetchExperiences()` methods. The SDK processes the metadata and returns the payload within the `ExperienceCampaignsResult` object.
* Identifying an Offering: You can identify an offering by inspecting the structure of the returned JSON. An offering payload consists of structured data nested under a specific custom offering key.
* Building the Offering UI: The SDK only returns this offering data as raw JSON. As the developer, you must write the logic to parse this JSON payload and build the corresponding visual UI components on the screen.
Use these methods to log "Clicks" when the user interacts with the UI element.
```javascript React Native wrap theme={null}
import ReactMoEngagePersonalize, { ExperienceCampaign } from "react-native-moengage-personalize";
const personalize = new ReactMoEngagePersonalize("YOUR_WORKSPACE_ID");
// campaign is an array of ExperienceCampaign objects
// Pass the exact objects or payloads previously received from the fetchExperiences() method.
const campaign: ExperienceCampaign = {/* campaign object */};
personalize.experienceClicked(campaign);
```
### 4b. Track Clicks for Offering Campaigns
For interactions with specific items (such as a product recommendation or a coupon) contained within an offering campaign:
```javascript React Native wrap theme={null}
import ReactMoEngagePersonalize, { ExperienceCampaign } from "react-native-moengage-personalize";
const personalize = new ReactMoEngagePersonalize("YOUR_WORKSPACE_ID");
const campaign: ExperienceCampaign = {/* campaign object */};
// object containing offering details for a specific offering campaign
// Pass the exact objects or payloads previously received from the fetchExperiences() method.
const offeringPayloads = { };
personalize.offeringClicked(campaign, offeringPayloads);
```
#### Example
```JSON Sample Payload theme={null}
{
"custom_offering_key": {
// Expected offering payload in the function call.
}
}
```
You can use these Offering-specific functions only if the campaign contains an offering payload (e.g., structured JSON data containing dynamic product recommendations, catalogs, or unique coupon codes nested under a specific offering key). For all other experience data, use the standard experience shown/clicked functions.
# FAQs
You can fetch up to 25 experiences in a single call. If you exceed this, the SDK returns the 25 most recently updated experiences and notifies you of the unfulfilled keys.
The SDK returns an empty payload along with a standardized error code (e.g., NETWORK\_ERROR).
# Android Notification Runtime Permissions
Source: https://moengage.com/docs/developer-guide/react-native-sdk/push/basic/android-notification-runtime-permissions
Handle Android 13 notification runtime permissions in your React Native app using the MoEngage SDK.
Android 13 (API level 33) and higher supports [runtime permission](https://developer.android.com/guide/topics/permissions/overview#runtime) for sending [non-exempt](https://developer.android.com/develop/ui/views/notifications/notification-permission#exemptions)(including Foreground Services (FGS)) notifications from an app [POST\_NOTIFICATIONS](https://developer.android.com/reference/android/Manifest.permission#POST_NOTIFICATIONS). This change helps users focus on the notifications that are most important to them.
Refer to the [official documentation](https://developer.android.com/develop/ui/views/notifications/notification-permission) for more details.
The below-mentioned APIs are supported starting MoEngage core Android SDK version **12.3.01**
When an application runs on Android 13 and wants to show notifications to the user, it must request the user's notification permission. You have two options: let MoEngage handle permissions for you or handle the notification permission with your code.
* MoEngage handles Notification permission.
* You just have to call a single line of code mentioned on this page.
* You maintain the notification permission logic.
* Notify MoEngage SDK if permission to push notifications is granted.
We recommend you let MoEngage handle push notification permissions.
## Let MoEngage SDK handle Notification permission
MoEngage SDK provides helper APIs to show the permission request to the end-user or navigate the user to the settings screen for enabling notifications.
Use the below API to show the permission request dialog to the user.
When using the below API SDK automatically tracks the response, sets up the required notification channel, etc, mentioned above.
```java JavaScript theme={null}
import ReactMoE from 'react-native-moengage'
ReactMoE.requestPushPermissionAndroid()
```
## Notify SDK of permission result
Once the application requests the user for notification permission notify the SDK of the user response using the below API. *If you are letting MoEngage SDK handle the notification permissions, you should ignore this step.*
```java JavaScript theme={null}
import ReactMoE from 'react-native-moengage'
ReactMoE.pushPermissionResponseAndroid(true);
```
## Update the Permission request count(optional)
Once the application requests the user for notification permission, update the SDK of the request attempts.
**Why does the SDK require permission attempt count?**
SDK requires the attempt count to accurately track the number of times the permission request was attempted. If the user denies the permission request twice the application/SDK cannot request permission further i.e. if we request permission it would be automatically denied without the user seeing the request dialog. As an SDK we cannot be sure of the number of times the user has denied the permission we try to optimize the calls to request permission based on the attempt count.
```java JavaScript theme={null}
import ReactMoE from 'react-native-moengage'
ReactMoE.updatePushPermissionRequestCountAndroid(requestCount);
```
## Navigate to Notification Settings (situational/optional)
Use the below API to show navigate the user to the Notification Settings for the application on the device.
*Note*: Below Android 8(API level 26) the user is directed to the Application Info screen of your application.
```java JavaScript theme={null}
import ReactMoE from 'react-native-moengage'
ReactMoE.navigateToSettingsAndroid()
```
# Android Push Configuration
Source: https://moengage.com/docs/developer-guide/react-native-sdk/push/basic/android-push-configuration
Set up and configure Android push notifications for your React Native app using MoEngage and Firebase.
# Basic Setup
Follow the basic setup outlined in this section to enable push notifications on an Android device using MoEngage.
* **FCM Setup on MoEngage Dashboard -** FCM Authentication is the method to enable sending push notifications to your app installed on Android devices. Use the methods mentioned in [this article](/docs/developer-guide/android-sdk/push/basic/fcm-authentication) to authenticate MoEngage to access Firebase services.
* **Adding metadata for push notification -** Set the small icon and large icon drawable and other options to handle push notifications using the methods available in [this article](/docs/developer-guide/components-for-sdk/push-notification/android-push-configuration-for-hybrid-applications).
* **Android Notification Runtime Permissions** - When the application is running on Android 13 to show notifications to the user, applications would need to request the user for notification permission. Refer to the methods available in [this article](/docs/developer-guide/components-for-sdk/push-notification/android-push-configuration-for-hybrid-applications#adding-metadata-for-push-notification) to handle permission requests.
* **Push Registration and Receiving** - To use Push Notification in your React Native application, you must configure Firebase. Configuring Firebase steps will depend on how you want to integrate it. MoEngage recommends leaving the push handling to MoEngage SDK, as the best practices are properly integrated. You can also handle the push at your app level. In any case, look at the following section that applies to you and finish the integration steps.
**Add messaging service**\
You must add the messaging service to the Manifest file for MoEngage SDK to show the notifications. Refer to this document [here](/docs/developer-guide/react-native-sdk/push/basic/android-push-configuration#passing-push-token).
**Callback on token registration (optional)**\
To get a callback whenever a new token is registered or refreshed, refer to the method [here](/docs/developer-guide/react-native-sdk/push/basic/android-push-configuration#passing-push-payload).
**Notification Clicked Callback**
To receive a callback whenever a push is clicked and for custom handling redirection, use the method in [this article](/docs/developer-guide/react-native-sdk/push/basic/android-push-configuration#customizing-push-notification).
**How to opt out of MoEngage Registration?**\
The MoEngage SDK attempts to register for a push token; since your application handles push, you need to opt out of SDK's token registration using the method in [this article](/docs/developer-guide/android-sdk/push/basic/push-token-registration-and-display#how-to-opt-out-of-moengage-push-token-registration).
**Pass the Push Token To MoEngage SDK** - After receiving the push token from FCM, use the method in [this article](/docs/developer-guide/android-sdk/push/basic/push-token-registration-and-display) to pass the Push Token to the MoEngage SDK to set it in the MoEngage platform.
**Passing the Push payload to the MoEngage SDK** - After receiving the push payload on the app, use the method in [this article](/docs/developer-guide/android-sdk/push/basic/push-token-registration-and-display#passing-the-push-payload-to-the-moengage-sdk) to send out push notifications to the device.
MoEngage recommends using the Android native APIs to pass the push payload to the MoEngage SDK instead of the React-Native/Javascript APIs. React-Native Engine might not get initialized if the application is killed or if the notification is not sent at a high priority.
* [Pass the Push Token To MoEngage SDK](/docs/developer-guide/react-native-sdk/push/basic/android-push-configuration)
* [Pass the Push payload to the MoEngage SDK](/docs/developer-guide/react-native-sdk/push/basic/android-push-configuration)
* [Callbacks and customizations](/docs/developer-guide/react-native-sdk/push/basic/android-push-configuration)
**Notification Clicked Callback -** MoEngage's React Native plugin optionally provides a callback on push clicks with the method in [this article](/docs/developer-guide/react-native-sdk/push/basic/push-callback).
# React-Native methods for Push
*You can skip this section completely if you let MoEngage handle push token registration and display or use Android Native methods to pass tokens and payload to MoEngage SDKs.*
Read on if you want to use React Native methods of MoEngage SDK to pass tokens and payload to MoEngage SDKs.
MoEngage recommends using the Android native APIs to pass the push payload to the MoEngage SDK instead of the React-Native/Javascript APIs. React-Native Engine might not get initialized if the application is killed or if the notification is not sent at a high priority.
## Passing Push Token
```javascript JavaScript theme={null}
const ReactMoE = require('react-native-moengage')
// pass the push token as a string
ReactMoE.passFcmPushToken("")
```
## Passing Push Payload
```javascript JavaScript theme={null}
const ReactMoE = require('react-native-moengage')
// pass the push payload as a JSONObject from FCM. Note only the data payload needs to be passed to SDK.
ReactMoE.passFcmPushPayload({})
```
MoEngage recommends that you use the Android native APIs for passing the push payload to the MoEngage SDK instead of the React-Native/Javascript APIs. React-Native Engine might not get initialized if the application is killed or if the notification is not sent at a high priority.
## Customizing Push notification
If required the application can customize the behavior of notifications by using Native Android code (Java/Kotlin). To learn more about the customization refer to the [Advanced Push Configuration](/docs/developer-guide/android-sdk/push/advanced/callbacks-and-customisation) documentation.Instead of extending ***PushMessageListener*** as mentioned in the above document extend ***PluginPushCallback.***
Refer to the below documentation for Push Amp+, Push Templates, and Geofence.
* [Push Amplification](/docs/developer-guide/android-sdk/push/optional/push-amplification)
* [Push Amp Plus](/docs/developer-guide/android-sdk/push/optional/push-amp-plus/push-amp-plus-integration)
* [Push Templates](/docs/developer-guide/android-sdk/push/optional/push-templates)
* [GeoFence Push](/docs/developer-guide/android-sdk/push/optional/location-triggered)
# Migrate To The Extension Integrator Tool
Source: https://moengage.com/docs/developer-guide/react-native-sdk/push/basic/integrating-moengage-service-and-content-extensions/migrating-to-the-extension-integrator-tool
Migrate from a manual iOS notification extension setup to the MoEngage Extension Integrator Tool in your React Native app.
All steps in this guide are performed in Xcode, open your iOS project by launching `ios/YourApp.xcworkspace` in Xcode before proceeding.
To migrate from an existing manual implementation to the integrator tool, follow the below steps:
### Step 1: Prerequisites
Before proceeding, ensure the following are in place:
* Ensure you are using MoEngage React Native SDK version [12.6.0](/docs/release-notes/sdks/react-native#core-12-6-0) or above to utilize the extension integrator tool.
* **SDK initialization:** Initialize the MoEngage SDK using [file-based initialization](/docs/developer-guide/react-native-sdk/sdk-integration/react-native/sdk-initialization/file-based-initialization/file-based-initialization).
* **App Group configuration:** Provide the `AppGroupName` key (for example, `group.com.organization.app`) you are using with your `sdkConfig.appGroupID`.
* After integration is complete, you may be prompted to access the keychain for code signing. Click **Always Allow.**
### Step 2: Integrate MoEngageRichNotification
Integrate `MoEngageRichNotification` if you need to support either of the following push notification features:
* **Rich media** — display images, GIFs, or video in the notification banner
* **Rich push templates** — render interactive notification layouts such as carousel
This step is mandatory if you have integrated the content extension.
To install the `MoEngageRichNotification` through SPM, perform the following steps:
1. Navigate to **File > Add Package**.
2. Enter the repository URL:
* `https://github.com/moengage/apple-sdk.git`
3. Select the **master** branch or a specific version and select **Add Package**.
4. Target the package to your application.
**Information** CocoaPods is being deprecated. MoEngage recommends using Swift Package Manager for all new integrations. For more info, refer [here](/docs/developer-guide/ios-sdk/sdk-integration/basic/sdk-integration).
1. Add the following line to your `Podfile` inside your app target.
The `react-native-moengage` package already brings in `MoEngage-iOS-SDK`, you only need to add the `RichNotification` subspec.
```ruby lines wrap theme={null}
pod 'MoEngage-iOS-SDK/RichNotification'
```
2. Run pod install:
```ruby lines wrap theme={null}
pod repo update
pod install
```
### Step 3: Integrate the extension integrator tool
Automate the extension configuration by adding a custom script to your build process.
1. In Xcode, select your application target, go to **Build Phases**, and click **+** to add a **New Run Script Phase**.
If you have already added a script phase and configured the input files, you can simply update the run command as described in step 3 below.
2. Add the following paths to the **Input Files** section:
* `$(BUILT_PRODUCTS_DIR)/$(INFOPLIST_PATH)`
* `$(INSTALL_DIR)/$(INFOPLIST_PATH)`
3. In the shell script input box of the Run Script Phase added in step 1, enter the command relevant to your dependency manager, replacing `$OPTIONS` with your desired configuration:
```bash Swift Package Manager (SPM) lines wrap theme={null}
${OBJROOT}/../../SourcePackages/artifacts/apple-sdk/moengage-extensions-integration/moengage-extensions-integration.artifactbundle/moengage-extensions-integration/bin/moengage-extensions-integration $OPTIONS
```
```bash CocoaPods lines wrap theme={null}
${PODS_ROOT}/MoEngageExtensionsIntegration/moengage-extensions-integration.artifactbundle/moengage-extensions-integration/bin/moengage-extensions-integration $OPTIONS
```
#### Available options
Replace `$OPTIONS` with one or more of the following:
| Option | Description |
| :--------------------------------------------------------------------- | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `--enable-push-notification-templates` | Required if the content extension from Step 2 is used. |
| `--notification-service-extension-name $CUSTOM_SERVICE_EXTENSION_NAME` | Sets a custom name for the service extension. Use this when migrating to this tool from a custom service extension implementation. (Default: `MoEngageNotificationService`). |
| `--notification-content-extension-name $CUSTOM_CONTENT_EXTENSION_NAME` | Sets a custom name for the content extension. Use this when migrating to this tool from a custom content extension implementation. (Default: `MoEngageNotificationContent`). |
### Step 3: Remove existing extensions
Remove existing service and content extensions added in **Frameworks**, **Libraries** and **Embedded Content** section.
Failing to remove prevents push delivery impressions and rich push notifications.
# Set Up iOS Notification Extensions for Rich Push and Impression Tracking
Source: https://moengage.com/docs/developer-guide/react-native-sdk/push/basic/integrating-moengage-service-and-content-extensions/set-up-ios-notification-extensions-for-rich-push-and-impression-tracking
Required configuration to enable push impression tracking and rich media notifications on iOS. Skipping this results in missing impression metrics and rich push failures.
All steps in this guide are performed in Xcode, open your iOS project by launching `ios/YourApp.xcworkspace` in Xcode before proceeding.
This guide describes how to integrate MoEngage service and content extensions into your iOS application. These extensions enable notification impression tracking, support for rich media (images, GIFs, and video), and the use of rich push notification templates.
## Integrating service and content extensions
Without these extensions, MoEngage cannot track push impressions, [rich media](https://www.moengage.com/docs/user-guide/campaigns-and-channels/mobile-push/notification-features-and-behavior/gifs-in-push-notifications) notifications fall back to plain text, and [rich push templates](https://www.moengage.com/docs/user-guide/campaigns-and-channels/mobile-push/create/push-templates) render with fallback template.
| Use Case | Service Extension (Step 2) | Content Extension (Step 3) | MoEngageRichNotification (Step 4) | Integrator Tool Flag |
| :--------------------------------------- | :------------------------- | :------------------------- | :-------------------------------- | :------------------------------------- |
| Push impression tracking | ✅ Required | — | — | (default) |
| Rich media in push (images, GIFs, video) | ✅ Required | — | ✅ Required | (default) |
| MoEngage rich push templates (carousel) | ✅ Required | ✅ Required | ✅ Required | `--enable-push-notification-templates` |
### Step 1: Prerequisites
Before proceeding, ensure the following are in place:
* Ensure you are using MoEngage React Native SDK version [12.6.0](/docs/release-notes/sdks/react-native#core-12-6-0) or above to utilize the extension integrator tool.
* **SDK initialization:** Initialize the MoEngage SDK using [file-based initialization](/docs/developer-guide/react-native-sdk/sdk-integration/react-native/sdk-initialization/file-based-initialization/file-based-initialization).
* **App Group configuration:** Define an App Group in your configuration using the `AppGroupName` key (for example, `group.com.organization.app`).
* Add this App Group to the **Signing & Capabilities** in Xcode.
* After integration is complete, you may be prompted to access the keychain for code signing. Click **Always Allow.**
### Step 2: Configure the service extension
The service extension tracks notification impressions and downloads rich media content.
1. [Create an App Identifier](https://developer.apple.com/help/account/identifiers/register-an-app-id): Use the format `[appBundleId].[serviceExtensionName].`
1. Replace `[appBundleId]` with your application’s specific bundle identifier
2. Replace `[serviceExtensionName]` with your chosen name (which defaults to *MoEngageNotificationService*). For example *:* If your app bundle identifier is `com.org.app` and your extension is named `NotificationService`, the identifier is `com.org.app.NotificationService`.
If you already have an existing Notification Service Extension, you can reuse it — you do not need to create a new one. Pass its name using the `--notification-service-extension-name` flag when configuring the [Run Script Phase in Step 5](#step-5-integrate-the-extension-integrator-tool). The tool will inject the necessary MoEngage logic without overwriting your custom code.
2. Select the service extension identifier created from step-1, open the **Capabilities** tab, add [**App Groups**](https://developer.apple.com/help/account/identifiers/register-an-app-group). Enter the name matching your `AppGroupName` key.
3. **Generate provisioning profile:** On the [Apple Developer Portal](https://developer.apple.com/account/resources/profiles/list), [create a new provisioning profile](https://developer.apple.com/help/account/provisioning-profiles/create-an-app-store-provisioning-profile) for the identifier created in step 1, and download it. To download it in Xcode, click **Xcode** in the menu bar, choose **Settings**.
4. Switch to the **Apple Accounts** section, and select your Apple developer account. On your Apple Accounts page, select **Download Manual Profiles**.
If you don't need rich push templates, you can skip Steps 3 and 4 and move directly to [Step 5: Integrate the extension integrator tool](#step-5-integrate-the-extension-integrator-tool).
### Step 3: Configure the content extension (Optional)
The content extension is required only if you intend to use MoEngage rich push notification templates.
1. [Create an App Identifier](https://developer.apple.com/help/account/identifiers/register-an-app-id): Use the format `[appBundleId].[contentExtensionName].`
1. Replace `[appBundleId]` with your application’s specific bundle identifier
2. Replace `[contentExtensionName]` with your chosen name (which defaults to *MoEngageNotificationContent*). For example\_:\_ If your app bundle identifier is `com.org.app` and your extension is named `NotificationContent`, the identifier is `com.org.app.NotificationContent`.
2. Select the content extension identifier, open the **Capabilities** tab, add [**App Groups**](https://developer.apple.com/help/account/identifiers/register-an-app-group). Enter the name matching your `AppGroupName` key.
3. **Generate provisioning profile:** On the [Apple Developer Portal](https://developer.apple.com/account/resources/profiles/list), [create a new provisioning profile](https://developer.apple.com/help/account/provisioning-profiles/create-an-app-store-provisioning-profile) for the identifier created in step 1, and download it. To download it in Xcode, click **Xcode** in the menu bar, choose **Settings**.
4. Switch to the **Apple Accounts** section, and select your Apple developer account. On your Apple Accounts page, select **Download Manual Profiles**.
### Step 4: Integrate MoEngageRichNotification (Optional)
Integrate `MoEngageRichNotification` if you need to support either of the following push notification features:
* **Rich media** — display images, GIFs, or video in the notification banner
* **Rich push templates** — render interactive notification layouts such as carousel
This step is mandatory if you have integrated the content extension mentioned in Step 3.
To install the `MoEngageRichNotification` through SPM, perform the following steps:
1. Navigate to **File > Add Package**.
2. Enter the repository URL:
* `https://github.com/moengage/apple-sdk.git`
3. Select the **master** branch or a specific version and select **Add Package**.
4. Target the package to your application.
**Information** CocoaPods is being deprecated. MoEngage recommends using Swift Package Manager for all new integrations. For more info, refer [here](/docs/developer-guide/ios-sdk/sdk-integration/basic/sdk-integration).
1. Add the following line to your `Podfile` inside your app target.
The `react-native-moengage` package already brings in `MoEngage-iOS-SDK` via autolinking — you only need to add the `RichNotification` subspec.
```ruby lines wrap theme={null}
pod 'MoEngage-iOS-SDK/RichNotification'
```
2. Run pod install:
```ruby lines wrap theme={null}
pod repo update
pod install
```
### Step 5: Integrate the extension integrator tool
Automate the extension configuration by adding a custom script to your build process.
1. In Xcode, select your application target, go to **Build Phases**, and click **+** to add a **New Run Script Phase**.
2. Add the following paths to the **Input Files** section:
* `$(BUILT_PRODUCTS_DIR)/$(INFOPLIST_PATH)`
* `$(INSTALL_DIR)/$(INFOPLIST_PATH)`
3. Enter the command relevant to your dependency manager, replacing **integrate\_extensions** in above image.
If you have already added a script phase and configured the input files, you can simply update the run command as described below.
```bash Swift Package Manager (SPM) lines wrap theme={null}
${OBJROOT}/../../SourcePackages/artifacts/apple-sdk/moengage-extensions-integration/moengage-extensions-integration.artifactbundle/moengage-extensions-integration/bin/moengage-extensions-integration $OPTIONS
```
```bash CocoaPods lines wrap theme={null}
${PODS_ROOT}/MoEngageExtensionsIntegration/moengage-extensions-integration.artifactbundle/moengage-extensions-integration/bin/moengage-extensions-integration $OPTIONS
```
#### Available options
Replace `$OPTIONS` with one or more of the following:
| Option | Description |
| :--------------------------------------------------------------------- | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `--enable-push-notification-templates` | Required if the content extension from Step 3 is used. |
| `--notification-service-extension-name $CUSTOM_SERVICE_EXTENSION_NAME` | Sets a custom name for the service extension. Use this when migrating to this tool from a custom service extension implementation. (Default: `MoEngageNotificationService`). |
| `--notification-content-extension-name $CUSTOM_CONTENT_EXTENSION_NAME` | Sets a custom name for the content extension. Use this when migrating to this tool from a custom content extension implementation. (Default: `MoEngageNotificationContent`). |
### Example Command (SPM)
For integrating a service extension named `NotificationService` and a content extension named `NotificationContent`, the complete command for SPM would be:
```bash Swift Package Manager (SPM) lines wrap theme={null}
${OBJROOT}/../../SourcePackages/artifacts/apple-sdk/moengage-extensions-integration/moengage-extensions-integration.artifactbundle/moengage-extensions-integration/bin/moengage-extensions-integration --enable-push-notification-templates --notification-service-extension-name NotificationService --notification-content-extension-name NotificationContent
```
4. **Disable sandboxing:** In your application target **Build Settings**, set **USER\_SCRIPT\_SANDBOXING** to **No**.
## Migrating to the extension integrator tool
To migrate from an existing manual implementation to the integrator tool, refer [here](/docs/developer-guide/react-native-sdk/push/basic/integrating-moengage-service-and-content-extensions/migrating-to-the-extension-integrator-tool).
## Troubleshooting and FAQs
Review the build logs for detailed information.
Common causes include:
* **Incorrect Integration:** The setup does not follow the [Integrating Service & Content Extension](/docs/developer-guide/ios-sdk/push/basic/ios-push-integration-tutorial) guide.
* **Missing Configuration:** The `Info.plist` is missing required MoEngage configuration options.
* **App Group Mismatch:** The `AppGroupName` provided in `Info.plist` is not included in the service and content extension bundle identifier capabilities or the application entitlements.
* **Environment Mismatch:** The provisioning profile was not created for the specific build environment (e.g., using a Development profile when generating an App Store build).
* **Custom Directory Issues:** Provisioning profiles are not stored in default Xcode directories. Use the [additional configuration build settings](#troubleshooting-and-faqs) to pass custom paths.
* **Certificate Issues:** Missing or incorrect certificate configuration during the generation of the provisioning profile.
* **Expired Profiles:** The provisioning profile has expired. This requires re-generating and re-downloading the profile.
No, The Service Extension is mandatory for tracking notification impressions and downloading rich media (images/GIFs/video). The Content Extension is only required if you intend to use MoEngage's interactive *Rich Push Templates* (e.g., carousels or custom button layouts).
iOS extensions run in a separate sandbox from your main application. The **App Group** creates a shared container that allows the MoEngage SDK in the main app to share authentication tokens, user data, and local storage with the extension. Without it, the extension cannot verify the user or track impressions correctly.
The **Extension Integrator Tool** needs to access the project’s built products and provisioning profiles located in system folders outside the standard Xcode sandbox. If `ENABLE_USER_SCRIPT_SANDBOXING` is set to **Yes**, the script will be blocked, resulting in a "Permission Denied" error during the build phase.
Additional build settings can be used to provide specific configuration inputs to the integrator tool for both local and CI builds.
* **`MOENGAGE_EXTENSION_PROFILES_SEARCH_PATHS`:** Use this to provide additional folders to scan for your provisioning profiles. By default, the tool scans standard Xcode directories:
* `~/Library/Developer/Xcode/UserData/Provisioning Profiles`
* `~/Library/MobileDevice/Provisioning Profiles` If your provisioning profiles are not present in these folders, add additional paths with this build setting.
* **`MOENGAGE_NOTIFICATION_SERVICE_EXTENSION_PROFILE`:** Provide the explicit provisioning profile filename for the Notification Service Extension. The tool will use this filename instead of searching for a provisioning profile. The profile must be present in one of the folders above. Use this option if the tool is not able to pick the right provisioning profile.
* **`MOENGAGE_NOTIFICATION_CONTENT_EXTENSION_PROFILE`:** Provide the explicit provisioning profile filename for the Notification Content Extension. The tool will use this filename instead of searching for a provisioning profile. The profile must be present in one of the folders above. Use this option if the tool is not able to pick the right provisioning profile.
This usually stems from one of three technical gaps:
1. **App Group Mismatch:** Ensure the `AppGroupName` string in your `Info.plist` matches the Entitlements file exactly.
2. **Payload Timeout:** iOS gives extensions \~30 seconds to download media. If your assets are too large or the network is slow, it will fail over to plain text.
Yes, but be careful with the SPM path in Step 5. The path `${OBJROOT}/../../SourcePackages/...` assumes a standard Xcode structure. If your CI environment (like Jenkins or Bitrise) uses a custom build directory, you may need to provide the absolute path to the `moengage-extensions-integration` binary.
# iOS Push Configuration
Source: https://moengage.com/docs/developer-guide/react-native-sdk/push/basic/ios-push-configuration
Configure iOS push notifications with APNS certificates and entitlements for your React Native app.
## Push Configuration
Following are the two ways to configure Push Notification
### APNS Authentication Key:
To send push notifications to iOS users, it is required to generate the APNs Auth Key file for your application and upload it to the MoEngage dashboard. Refer the [link](https://www.moengage.com/docs/developer-guide/ios-sdk/push/basic/apns-authentication-key) to generate Auth key.
### APNS Certificate:
First you will have to create an APNS certificate and upload in the dashboard to be able to send push notifications in iOS. Follow the steps below to do that :
* [Create an APNS certificate](https://www.moengage.com/docs/developer-guide/ios-sdk/push/basic/apns-certificate-pem-file-legacy#create-an-apns-certificate-in-developer-account)
* [Convert the resultant certificate to .pem format](https://www.moengage.com/docs/developer-guide/ios-sdk/push/basic/apns-certificate-pem-file-legacy#converting-certificate-to-pem-format)
* [Upload .pem file to MoEngage Dashboard](https://www.moengage.com/docs/developer-guide/ios-sdk/push/basic/apns-certificate-pem-file-legacy#uploading-pem-file-to-moengage-dashboard)
## Adding Push Entitlement to your Project:
Once the APNS Certificate is uploaded, enable Push Entitlement in the Xcode project. For that select your app target, then go to Capabilities. Here enable the Push Notifications capability for your app as shown below :
## Uninstall Tracking:
We make use of silent pushes to track uninstalls. For tracking uninstalls of all the users, enable Remote Notification background mode in-app capabilities for the same as shown below :
## Push Registration:
After this you will have to register for push notification by using **registerForPush** method of the plugin as shown below :
```javascript JavaScript theme={null}
//This is only for iOS
import ReactMoE from 'react-native-moengage'
ReactMoE.registerForPush();
```
Plugin gets all the remote notification-related callbacks, therefore you won't receive any of them in your AppDelegate. Therefore, you will have to add observers for the notifications provided by plugin instead.
## Provisional Push Registration:
* This feature is supported from version ***11.1.0*** of the plugin.
To register for provisional push notification, call ***registerForProvisionalPush*** Api of the plugin as shown below
```javascript JavaScript theme={null}
import ReactMoE from 'react-native-moengage'
// This API is only for iOS
ReactMoE.registerForProvisionalPush();
```
## Rich Push and Templates Support:
To support Rich Push (images/videos/audio in the notification) and Templates in your React Native app, set up the iOS Notification Service and Content Extensions:
* [Integrate Service and Content Extension](developer-guide/react-native-sdk/push/basic/integrating-moengage-service-and-content-extensions/set-up-ios-notification-extensions-for-rich-push-and-impression-tracking)
or you can manually integrate Rich Push and Push Templates. For more information, refer the below docs:
* [Rich Push](https://www.moengage.com/docs/developer-guide/ios-sdk/push/basic/ios-push-integration-tutorial#optional)
* [Push Templates](https://www.moengage.com/docs/developer-guide/ios-sdk/push/optional/push-templates)
# Push Callback
Source: https://moengage.com/docs/developer-guide/react-native-sdk/push/basic/push-callback
Set up push token and push click callback listeners in the MoEngage React Native SDK.
# Push Token Callback
MoEngage plugin triggers the`pushTokenGenerated` event whenever device token is generated. This event is a common trigger for both iOS and Android platforms and is available from plugin version `6.0.0`. Refer to the below code to set listener to the same:
```typescript TypeScript theme={null}
import ReactMoE from 'react-native-moengage'
ReactMoE.setEventListener("pushTokenGenerated", (payload) => {
console.log("pushTokenGenerated", payload);
});
```
Payload received in the callback is a `MoEPushToken` instance with the following definition:
```typescript TypeScript theme={null}
class MoEPushToken {
/// Native platform from which the callback was triggered.(ios/android)
platform: MoEPlatform;
/// push type associated with platform
pushService: MoEPushService;
/// push token value
token: String;
}
```
# Push Click Callback
MoEngage plugin triggers the`pushClicked` event whenever a notification is clicked. This event is a common trigger for both iOS and Android platforms and is available from plugin version `6.0.0`. Refer to the below code to set the listener to the same:
Make sure you are calling `initialize()` method of the MoEngage plugin after you set up these callbacks. Refer [Initialise React-Native Component](/docs/developer-guide/react-native-sdk/sdk-integration/react-native/sdk-initialization/manual-initialization/framework-initialization) for more info.
```typescript TypeScript theme={null}
import ReactMoE from 'react-native-moengage'
ReactMoE.setEventListener("pushClicked", (notificationPayload) => {
console.log("pushClicked", notificationPayload);
});
```
Make sure this callback is set as soon as the application is initialized. Preferably in the constructor of your `App.js`.
NotificationPayload received in the callback is a `MoEPushPayload` instance with the following definition:
```typescript TypeScript theme={null}
class MoEPushPayload {
/// account payload for which callback is received
accountMeta: MoEAccountMeta;
/// Push click payload
data: MoEPushCampaign;
/// Native platform from which the callback was triggered.(ios/android)
platform: MoEPlatform;
}
class MoEPushCampaign {
/// notification payload
payload: Map
///boolean value indicating if the user clicked on the default content or not.
isDefaultAction: Boolean;
///Action to be performed on notification click.
clickAction: Map;
}
```
Payload Structure for `clickedAction` Map
```json JSON theme={null}
{
"clickedAction": {
"type": "navigation/customAction",
"payload": {
"type": "screenName/deepLink/richLanding",
"value": "",
"kvPair": {
"key1": "value1",
"key2": "value2",
...
}
}
}
}
```
* `platform` - Native platform from which callback is triggered. Possible values - `android`, `ios`
* `isDefaultAction` - This key is present only for the Android Platform. It's a boolean value indicating if the user clicked on the default content or not. true if the user clicks on the default content else false.
* `clickedAction` - Action to be performed on notification click.
* `clickedAction.type` - Type of click action. Possible values `navigation` and `customAction`. Currently, `customAction` is supported only on Android.
* `clickAction.payload` - Action payload for the clicked action.
* `clickedAction.payload.type` - Type of navigation action defined. Possible values `screenName`, `deepLink`, `richLanding`. Currently, in the case of iOS, richlanding and deep-link URL are processed internally by the SDK and not passed in this callback therefore possible value in case of iOS is only `screenName`.
* `clickAction.value` - value entered for navigation action or custom payload.
* `clickAction.kvPair` - Custom key-value pair entered on the MoEngage Platform.
* `payload` - Complete campaign payload.
## Android Payload
If the user clicks on the default content of the notification the key-value pair and campaign payload can be found inside the `payload` key. If the user clicks on the action button or a push template action the action payload would be found inside `clickedAction`.\
You can use the `isDefaultAction` key to check whether the user clicked on the default content or not and then parse the payload accordingly.
## iOS Payload
In the case of iOS, you would always receive the key-value pairs with respect to clicked action in `clickedAction` the property. Refer to this [Notification Payload](/docs/developer-guide/ios-sdk/push/basic/ios-push-integration-tutorial#step-1-implement-notification-service-extension-nse) to know the iOS notification payload structure.
# Self-Handled Push Click Android (Optional)
By default, when the user clicks on a notification the SDK redirects the user to the defined Activity and passes the callback to the Application to load the specific react-native component.
When the application is in the foreground it might seem like the application is reloading and not a very good user experience. You might just want to navigate the user to the specific react-native component. To do so follow the below steps.
While initializing the React-Native Plugin, enable foreground click callback in the ***MoEPushConfig*** object.
```typescript TypeScript theme={null}
import { MoEPushConfig, MoEInitConfig } from "react-native-moengage";
ReactMoE.initialize(
"YOUR_WORKSPACE_ID",
new MoEInitConfig(new MoEPushConfig(true))
);
```
You must call the ***initialize()*** when the React Component is mounted and the application comes to the foreground.
Enable the Self-Handled callback in the SDK initialization in the Application class as shown below
```kotlin Kotlin theme={null}
val moEngage = MoEngage.Builder(this, "YOUR_WORKSPACE_ID")
MoEInitializer.initializeDefaultInstance(applicationContext, moEngage, true)
```
```java Java theme={null}
MoEngage.Builder moEngage = new MoEngage.Builder(this, "YOUR_WORKSPACE_ID");
MoEInitializer.INSTANCE.initializeDefaultInstance(getApplicationContext(), moEngage, true);
```
Add the below Activity to your AndroidManifest.xml
```xml XML theme={null}
```
Notification click callbacks that are triggered when the application is in the foreground(and the above flow is enabled) will have an additional key i.e. ***selfHandledPushRedirection*** with value as ***true***
# Location Triggered
Source: https://moengage.com/docs/developer-guide/react-native-sdk/push/optional/location-triggered
Install and configure the MoEngage Geofence SDK for location-triggered push in React Native.
# Installation
To add MoEngage Geofence SDK to your application run the below command from a terminal
```NPM npm theme={null}
npm install react-native-moengage-geofence
```
Note: This plugin is dependent on `react-native-moengage` plugin. Make sure you have installed the `react-native-moengage` plugin as well.
## Android Installation
For location triggered push to work, ensure your Application has:
* Location permission
* Play Services Location Library
* Device's location should be enabled
## iOS Installation
To run the application in the new react architecture, follow these steps:
1. Navigate to the iOS folder.
2. Run the command ***RCT\_NEW\_ARCH\_ENABLED=1 bundle exec pod install*** to install the necessary dependencies.
To run the application in the old react architecture, follow these steps
1. Navigate to the iOS folder.
2. Run the command ***pod install*** to install the necessary dependencies.
# Configure Geofence
By default, the geofence feature is not enabled. To enable the feature call the below API.
```TypeScript TypeScript theme={null}
import ReactMoEGeofence from 'react-native-moengage-geofence';
ReactMoEGeofence.startGeofenceMonitoring(YOUR_WORKSPACE_ID);
```
At any time if you want to stop the geofence monitoring or feature use the below API. This API will remove the existing geofences.
```TypeScript TypeScript theme={null}
import ReactMoEGeofence from 'react-native-moengage-geofence';
ReactMoEGeofence.stopGeofenceMonitoring(YOUR_WORKSPACE_ID);
```
# Notification Center
Source: https://moengage.com/docs/developer-guide/react-native-sdk/push/optional/notification-center
Install and set up the MoEngage Inbox plugin to build a notification center in React Native.
# Installation
Install MoEngage's Inbox Plugin to your application, using the npm package manager. And then link your native dependencies.
```shell Shell theme={null}
$ npm install react-native-moengage-inbox
# required only if you are using versions that do not support auto linking
# This command is removed in version 0.69 of react-native
$ react-native link react-native-moengage-inbox
```
Note: This plugin is dependent on `react-native-moengage` plugin. Make sure you have installed the `react-native-moengage` plugin as well. Refer to the [link](https://developers.moengage.com/hc/en-us/articles/4404205340564) for the same.
## iOS Installation
To run the application in the new react architecture, follow these steps:
1. Navigate to the iOS folder.
2. Run the command ***RCT\_NEW\_ARCH\_ENABLED=1 bundle exec pod install*** to install the necessary dependencies.
To run the application in the old react architecture, follow these steps:
1. Navigate to the iOS folder.
2. Run the command ***pod install*** to install the necessary dependencies.
Make sure to configure [AppGroup ID in App Target](https://developers.moengage.com/hc/en-us/articles/4403905438228) and Set up [Notification Service Extension](https://developers.moengage.com/hc/en-us/articles/43960004257428-iOS-Push-Integration-Tutorial#h_01K45FKD0XDH4X776R47JA04GJ) in your iOS Project, for the SDK to save the received notifications.
# Inbox Initialization
To initialise Inbox, pass Workspace ID as parameter to `initialize(YOUR_WORKSPACE_ID)` method of `MoEReactInbox` as shown below
```typescript TypeScript theme={null}
import MoEReactInbox from "react-native-moengage-inbox";
MoEReactInbox.initialize(YOUR_WORKSPACE_ID);
```
# Fetch Messages
To fetch all the inbox messages use `fetchAllMessages()` method as shown below, where you would get an instance of `MoEInboxData`
```typescript TypeScript theme={null}
import MoEReactInbox from "react-native-moengage-inbox";
var inboxData= await MoEReactInbox.fetchAllMessages()
```
## InboxData Payload
MoEInboxData will be received in the below format:
```typescript TypeScript theme={null}
class MoEInboxData {
/// Native platform from which the callback was triggered.(ios/android)
platform: String;
/// List of [MoEInboxMessage]
messages:Array = [];
}
class MoEInboxMessage {
/// internal identifier used by the SDK for storage.(Only Android)
id: number;
/// Unique identifier for a message.
campaignId: string;
/// Text content of the message. Instance of MoETextContent
text: MoETextContent;
/// true if the message has been clicked by the user else false
isClicked: boolean;
/// Media content associated with the message.
media: MoEMedia;
/// List of actions to be executed on click. Instances of [MoEAction]
action: Array = [];
/// Tag associated with the message.
tag: string;
/// The time in which the message was received on the device.
///
/// Format - ISO-8601 yyyy-MM-dd'T'HH:mm:ss'Z'
receivedTime: string;
/// The time at which the message expiry.
///
/// Format - ISO-8601 yyyy-MM-dd'T'HH:mm:ss'Z'
expiry: string;
/// Complete message payload. This will vary for platforms
payload: Map;
/// A key representing the group to which the inbox message belongs.
/// @since 6.0.0
groupKey: string | null;
/// Notification Replacement Id.
/// @since 6.0.0
notificationId: string | null;
/// The timestamp indicating when the message was sent.
/// Format - ISO-8601 yyyy-MM-dd'T'HH:mm:ss'Z'
/// @since 6.0.0
sentTime: string | null;
}
class MoEAction {
/// actionType- navigation
actionType: MoEActionType;
/// navigationType- deepLink, richLanding, screenName
navigationType: string;
/// Value associated with navigation action eg: url / screen name
value: string;
/// Custom Key-Value Pairs associated with action
kvPair?: Map;
}
class MoEMedia {
/// Content type of the Media. (image/video/audio)
mediaType: MoEMediaType;
/// Url for the media content. Generally a http(s) url.
url: string;
/// Accessibility information associated with media content.
/// @since 6.0.0
accessibilityData: MoEAccessibilityData | null;
}
class MoETextContent {
/// Tiitle content of the message
title: string;
/// Subtitle content of the message
subtitle?: string;
/// Message content of the message
message: string;
/// Summary content of the message
summary?: string;
}
/// @since 12.0.0 of react-native-moengage package
class MoEAccessibilityData {
/// The accessibility text
text: string | null;
/// The accessibility hint
hint: string | null;
}
```
# Get Unclicked Message Count
To obtain the unclicked messages count from the Inbox use `getUnClickedCount()` method as shown below:
```typescript TypeScript theme={null}
import MoEReactInbox from "react-native-moengage-inbox";
var count = await MoEReactInbox.getUnClickedCount()
```
# Track Message Clicks
To track clicks on the messages inside your Inbox use `trackMessageClicked()` method as shown below:
```typescript TypeScript theme={null}
import MoEReactInbox from "react-native-moengage-inbox";
MoEReactInbox.trackMessageClicked(message) //Pass the instance of MoEInboxMessage here
```
# Delete Message
To delete a particular message from the list of messages use `deleteMessage()` method as shown below:
```typescript TypeScript theme={null}
import MoEReactInbox from "react-native-moengage-inbox";
MoEReactInbox.deleteMessage(message) //Pass the instance of MoEInboxMessage here
```
# Notification Center
Source: https://moengage.com/docs/developer-guide/react-native-sdk/push/optional/notification-triggered
Install the MoEngage Inbox plugin for notification-triggered messaging in your React Native app.
# Installation
Install MoEngage's Inbox Plugin to your application, using the npm package manager. And then link your native dependencies.
```shell Shell theme={null}
$ npm install react-native-moengage-inbox
# required only if you are using versions that do not support auto linking
# This command is removed in version 0.69 of react-native
$ react-native link react-native-moengage-inbox
```
Note: This plugin is dependent on `react-native-moengage` plugin. Make sure you have installed the `react-native-moengage` plugin as well. Refer to the [link](https://www.moengage.com/docs/developer-guide/react-native-sdk/sdk-integration/react-native/sdk-installation/framework-dependency) for the same.
## Android Installation
### Configuration Required For Older React Version (Optional)
This step is required only if react-native auto-linking is not working.
In ***android/settings.gradle(.kts)*** add the following:
```shell Groovy theme={null}
include ':react-native-moengage-inbox'
project(':react-native-moengage-inbox').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-moengage-inbox/android')
```
In ***android/app/build.gradle(.kts)*** add the following
```shell Groovy theme={null}
dependencies {
...
implementation project(':react-native-moengage-inbox')
}
```
Add the MoEngage React Package in the Application class's `getPackages()`
Path - ***android/app/src/main/java/package-name/MainApplication.java***
Note: Your Application class name might vary, go to your application class.
```java Java theme={null}
public class MainApplication extends Application implements ReactApplication {
private final ReactNativeHost mReactNativeHost = new ReactNativeHost(this) {
@Override
protected boolean getUseDeveloperSupport() {
return BuildConfig.DEBUG;
}
@Override
protected List getPackages() {
List packages = new PackageList(this).getPackages();
packages.add(new MoengageInboxPackage());
return packages;
}
}
};
@Override public void onCreate() {
super.onCreate();
}
@Override
public ReactNativeHost getReactNativeHost() {
return mReactNativeHost;
}
}
```
In case you are facing issues with the import add the below import statement in your java file.
```java Java theme={null}
import com.moengage.react.inbox.MoengageInboxPackage;
```
## iOS Installation
To run the application in the new react architecture, follow these steps:
1. Navigate to the iOS folder.
2. Run the command ***RCT\_NEW\_ARCH\_ENABLED=1 bundle exec pod install*** to install the necessary dependencies.
To run the application in the old react architecture, follow these steps:
1. Navigate to the iOS folder.
2. Run the command ***pod install*** to install the necessary dependencies.
Make sure to configure [AppGroup ID in App Target](https://www.moengage.com/docs/developer-guide/ios-sdk/sdk-integration/basic/sdk-initialization) and Set up [Notification Service Extension](https://www.moengage.com/docs/developer-guide/ios-sdk/push/basic/ios-push-integration-tutorial#step-1-implement-notification-service-extension-nse) in your iOS Project, for the SDK to save the received notifications.
# Inbox Initialization
To initialise Inbox, pass Workspace ID as parameter to `initialize(YOUR_WORKSPACE_ID)` method of `MoEReactInbox` as shown below
```typescript TypeScript theme={null}
import MoEReactInbox from "react-native-moengage-inbox";
MoEReactInbox.initialize(YOUR_WORKSPACE_ID);
```
# Fetch Messages
To fetch all the inbox messages use `fetchAllMessages()` method as shown below, where you would get an instance of `MoEInboxData`
```typescript TypeScript theme={null}
import MoEReactInbox from "react-native-moengage-inbox";
var inboxData= await MoEReactInbox.fetchAllMessages()
```
## InboxData Payload
MoEInboxData will be received in the below format:
```typescript TypeScript theme={null}
class MoEInboxData {
/// Native platform from which the callback was triggered.(ios/android)
platform: String;
/// List of [MoEInboxMessage]
messages:Array = [];
}
class MoEInboxMessage {
/// internal identifier used by the SDK for storage.(Only Android)
id: number;
/// Unique identifier for a message.
campaignId: string;
/// Text content of the message. Instance of MoETextContent
text: MoETextContent;
/// true if the message has been clicked by the user else false
isClicked: boolean;
/// Media content associated with the message.
media: MoEMedia;
/// List of actions to be executed on click. Instances of [MoEAction]
action: Array = [];
/// Tag associated with the message.
tag: string;
/// The time in which the message was received on the device.
///
/// Format - ISO-8601 yyyy-MM-dd'T'HH:mm:ss'Z'
receivedTime: string;
/// The time at which the message expiry.
///
/// Format - ISO-8601 yyyy-MM-dd'T'HH:mm:ss'Z'
expiry: string;
/// Complete message payload. This will vary for platforms
payload: Map;
/// A key representing the group to which the inbox message belongs.
/// @since 6.0.0
groupKey: string | null;
/// Notification Replacement Id.
/// @since 6.0.0
notificationId: string | null;
/// The timestamp indicating when the message was sent.
/// Format - ISO-8601 yyyy-MM-dd'T'HH:mm:ss'Z'
/// @since 6.0.0
sentTime: string | null;
}
class MoEAction {
/// actionType- navigation
actionType: MoEActionType;
/// navigationType- deepLink, richLanding, screenName
navigationType: string;
/// Value associated with navigation action eg: url / screen name
value: string;
/// Custom Key-Value Pairs associated with action
kvPair?: Map;
}
class MoEMedia {
/// Content type of the Media. (image/video/audio)
mediaType: MoEMediaType;
/// Url for the media content. Generally a http(s) url.
url: string;
/// Accessibility information associated with media content.
/// @since 6.0.0
accessibilityData: MoEAccessibilityData | null;
}
class MoETextContent {
/// Tiitle content of the message
title: string;
/// Subtitle content of the message
subtitle?: string;
/// Message content of the message
message: string;
/// Summary content of the message
summary?: string;
}
/// @since 12.0.0 of react-native-moengage package
class MoEAccessibilityData {
/// The accessibility text
text: string | null;
/// The accessibility hint
hint: string | null;
}
```
# Get Unclicked Message Count
To obtain the unclicked messages count from the Inbox use `getUnClickedCount()` method as shown below:
```typescript TypeScript theme={null}
import MoEReactInbox from "react-native-moengage-inbox";
var count = await MoEReactInbox.getUnClickedCount()
```
# Track Message Clicks
To track clicks on the messages inside your Inbox use `trackMessageClicked()` method as shown below:
```typescript TypeScript theme={null}
import MoEReactInbox from "react-native-moengage-inbox";
MoEReactInbox.trackMessageClicked(message) //Pass the instance of MoEInboxMessage here
```
# Delete Message
To delete a particular message from the list of messages use `deleteMessage()` method as shown below:
```typescript TypeScript theme={null}
import MoEReactInbox from "react-native-moengage-inbox";
MoEReactInbox.deleteMessage(message) //Pass the instance of MoEInboxMessage here
```
# React Native Sample App
Source: https://moengage.com/docs/developer-guide/react-native-sdk/sample-app/react-native-sample-app
Explore the MoEngage React Native sample application as a reference for integrating the SDK.
The [MoEngage React Native Sample application](https://github.com/moengage/React-Native/tree/master/SampleApp) offers a useful reference point for integrating MoEngage into your React Native app.
## Next Steps
* [SDK Installation](/docs/developer-guide/react-native-sdk/sdk-integration/react-native/sdk-installation/framework-dependency)
* [Framework Initialization](/docs/developer-guide/react-native-sdk/sdk-integration/react-native/sdk-initialization/manual-initialization/framework-initialization)
# JWT Authentication
Source: https://moengage.com/docs/developer-guide/react-native-sdk/sdk-integration/advanced/jwt-authentication
Secure your MoEngage data collection by implementing JWT authentication in your React Native application.
## Overview
JWT (JSON Web Token) authentication is a standard method for securely verifying user identity. By implementing JWT authentication, you add a critical layer of security to your data collection process with MoEngage.
The feature ensures that the data sent on behalf of your identified users is authentic and has not been tampered with. This security is achieved by requiring a token that is cryptographically signed by your own server, which prevents unauthorized users from impersonating your legitimate users.
**Prerequisites**
Before you begin the implementation, ensure you meet the following requirements:
* Your application must use the MoEngage React Native Core plugin version [***12.9.1***](/docs/release-notes/sdks/react-native#core-12-9-1) or higher to access the JWT authentication feature.
* You must have access to your MoEngage dashboard to manage public keys and configure the feature's enforcement settings. For detailed information on enforcement settings, [refer here](/docs/user-guide/settings/account/security/sdk-authentication#step-2-select-an-enforcement-mode).
The following diagram illustrates the interaction between your application, your server, the MoEngage SDK, and the MoEngage server:
## Integration
Perform the following to integrate JWT authentication into your React Native application.
### Step 1: Enable JWT Authentication
Enable JWT authentication during native SDK initialization on each platform. Follow the instructions that match the initialization method your application uses. Steps 2 and 3 are the same for both methods.
If you use the [config generator](https://app-cdn.moengage.com/sdk/integration/config/index.html) to produce your configuration files, set **Enable JWT Authorisation** to **Yes**. The generated files then contain the keys described below.
#### Android
**Manual Initialization**
Configure the ***NetworkAuthorizationConfig*** property on the ***MoEngage.Builder*** object. For more information, refer to [Android SDK Initialization](/docs/developer-guide/react-native-sdk/sdk-integration/react-native/sdk-initialization/manual-initialization/android).
```kotlin Kotlin wrap theme={null}
import com.moengage.core.DataCenter
import com.moengage.core.MoEngage
import com.moengage.core.config.NetworkAuthorizationConfig
import com.moengage.core.config.NetworkRequestConfig
import com.moengage.react.MoEInitializer
val moEngage = MoEngage.Builder(this, "YOUR_WORKSPACE_ID", DataCenter.DATA_CENTER_X)
.configureNetworkRequest(NetworkRequestConfig(NetworkAuthorizationConfig(isJwtEnabled = true)))
MoEInitializer.initializeDefaultInstance(applicationContext, moEngage)
```
```java Java wrap theme={null}
import com.moengage.core.DataCenter;
import com.moengage.core.MoEngage;
import com.moengage.core.config.NetworkAuthorizationConfig;
import com.moengage.core.config.NetworkRequestConfig;
import com.moengage.react.MoEInitializer;
MoEngage.Builder builder = MoEngage.builder(this, "YOUR_WORKSPACE_ID", DataCenter.getDataCenterX())
.configureNetworkRequest(new NetworkRequestConfig(new NetworkAuthorizationConfig(true)));
MoEInitializer.INSTANCE.initializeDefaultInstance(getApplicationContext(), builder);
```
**File-Based Initialization**
Add the following key to your `moengage.xml` configuration file. For more information, refer to [File Based Initialization](/docs/developer-guide/react-native-sdk/sdk-integration/react-native/sdk-initialization/file-based-initialization/file-based-initialization#android-configuration-reference).
```xml moengage.xml theme={null}
true
```
#### iOS
**Manual Initialization**
Configure the ***networkConfig*** property on the ***MoEngageSDKConfig*** object. For more information, refer to [iOS SDK Initialization](/docs/developer-guide/react-native-sdk/sdk-integration/react-native/sdk-initialization/manual-initialization/ios).
```swift Swift wrap theme={null}
let sdkConfig = MoEngageSDKConfig(appId: "YOUR_WORKSPACE_ID", dataCenter: .YOUR_DATA_CENTER)
sdkConfig.networkConfig = MoEngageNetworkRequestConfig(authorizationConfig: MoEngageNetworkAuthorizationConfig(isJwtEnabled: true))
MoEngageInitializer.sharedInstance().initializeDefaultSDKConfig(sdkConfig, andLaunchOptions: launchOptions)
```
```objective-c Objective-C wrap theme={null}
MoEngageSDKConfig* sdkConfig = [[MoEngageSDKConfig alloc] initWithAppId:@"YOUR_WORKSPACE_ID" dataCenter:YOUR_DATA_CENTER];
sdkConfig.networkConfig = [[MoEngageNetworkRequestConfig alloc] initWithAuthorizationConfig:[[MoEngageNetworkAuthorizationConfig alloc] initWithIsJwtEnabled:YES]];
[[MoEngageInitializer sharedInstance] initializeDefaultSDKConfig:sdkConfig andLaunchOptions:launchOptions];
```
**File-Based Initialization**
Add the following key to the `MoEngage` dictionary in your `Info.plist`. For more information, refer to [File Based Initialization](/docs/developer-guide/react-native-sdk/sdk-integration/react-native/sdk-initialization/file-based-initialization/file-based-initialization#ios-configuration-reference).
```xml Info.plist theme={null}
IsJwtEnabled
```
### Step 2: Pass the JWT to the SDK
Your application is responsible for managing the JWT lifecycle. The recommended flow is to fetch a token when the user logs in and pass the token to the SDK. You should also check whether the token has expired on subsequent app launches and fetch a new one if necessary.
Use the ***passAuthenticationDetails()*** method to provide the token to the SDK.
```typescript TypeScript wrap theme={null}
import ReactMoE, {
MoEAuthenticationType,
MoEJwtAuthenticationData,
} from "react-native-moengage";
ReactMoE.passAuthenticationDetails({
authenticationType: MoEAuthenticationType.JWT,
data: new MoEJwtAuthenticationData("YOUR_JWT_TOKEN", "USER_IDENTIFIER"),
});
```
For detailed information, refer to [Interfaces and Enums](#interfaces-and-enums).
### Step 3: Register the Listener and Handle Authentication Errors
The SDK delivers token validation errors returned by the MoEngage server through the `authenticationError` event. Register a listener for this event so your application can fetch and provide a new token when authentication fails.
Register the listener in a global scope, such as your root component, so your application always receives callbacks.
```typescript TypeScript wrap theme={null}
import ReactMoE, {
MoEAuthenticationType,
MoEAuthenticationErrorData,
MoEJwtAuthenticationErrorData,
} from "react-native-moengage";
ReactMoE.setEventListener(
"authenticationError",
(error: MoEAuthenticationErrorData) => {
if (error.authenticationType === MoEAuthenticationType.JWT) {
const errorData = error.data as MoEJwtAuthenticationErrorData;
const jwtError = errorData.code;
const message = errorData.message;
// Take appropriate action based on jwtError.
// For example, fetch a new token and call passAuthenticationDetails() again.
}
}
);
```
To stop receiving the callback, call ***removeEventListener()*** with the same event name.
```typescript TypeScript wrap theme={null}
ReactMoE.removeEventListener("authenticationError");
```
For detailed information, refer to [Interfaces and Enums](#interfaces-and-enums).
## Interfaces and Enums
The following interfaces, classes, and enums define the data structures used by the JWT authentication methods described in this guide. Use them when constructing your token payload and handling errors.
```typescript TypeScript wrap theme={null}
// Payload accepted by passAuthenticationDetails().
interface MoEAuthenticationData {
authenticationType: MoEAuthenticationType;
data: MoEAuthenticationDetails; // For JWT, use MoEJwtAuthenticationData.
}
// Authentication scheme used to authenticate the SDK's network requests.
enum MoEAuthenticationType {
JWT = "JWT",
}
// JWT specific authentication payload.
// Construct with: new MoEJwtAuthenticationData(token, userIdentifier)
interface MoEJwtAuthenticationData extends MoEAuthenticationDetails {
token: string;
userIdentifier: string;
}
// Payload delivered with the authenticationError event.
interface MoEAuthenticationErrorData {
accountMeta: MoEAccountMeta;
platform: MoEPlatform;
authenticationType: MoEAuthenticationType;
data: MoEAuthenticationErrorDetails; // For JWT, use MoEJwtAuthenticationErrorData.
}
// JWT specific error details.
interface MoEJwtAuthenticationErrorData extends MoEAuthenticationErrorDetails {
code: MoEJwtErrorCode;
token: string;
userIdentifier: string;
message: string;
}
// Reason the JWT authentication failed.
enum MoEJwtErrorCode {
TimeConstraintFailure = "TIME_CONSTRAINT_FAILURE",
DecryptionFailed = "DECRYPTION_FAILED",
HeaderTypeIncompatible = "HEADER_TYPE_INCOMPATIBLE",
PayloadContentMissing = "PAYLOAD_CONTENT_MISSING",
InvalidSignature = "INVALID_SIGNATURE",
IdentifierMismatch = "IDENTIFIER_MISMATCH",
Unknown = "UNKNOWN",
TokenNotAvailable = "TOKEN_NOT_AVAILABLE",
}
// Platform on which the error occurred.
enum MoEPlatform {
Android = "android",
IOS = "iOS",
}
```
**Information**
* If an API request fails due to an authentication error, the SDK will not retry the request until your application provides a new token.
* After 10 consecutive authentication failures in a single session, the SDK will stop attempting to sync data until the next session begins. This counter resets after any successful sync.
* Upon user logout, if a data sync fails due to a JWT error, the pending data will be deleted, and no retry will be attempted.
# Configure the MoEngage Expo SDK
Source: https://moengage.com/docs/developer-guide/react-native-sdk/sdk-integration/expo/configure-the-moengage-expo-sdk
Configure the MoEngage Expo SDK plugin parameters and platform-specific initialization files.
## Overview
This guide covers the parameters you can use to configure the MoEngage SDK. All plugin-level configuration is done within your app.json or app.config.js file. Core SDK credentials are placed in separate platform-specific files.
You must complete the [installation](/docs/developer-guide/react-native-sdk/sdk-integration/expo/installation) guide before you configure the SDK.
## Plugin-Managed Native Configuration
To ensure a reliable and streamlined setup, the MoEngage Expo plugin manages native project configurations automatically. This approach reduces the potential for manual errors by handling platform-specific requirements during the prebuild process.
Key automations include:
* Applying Critical Settings: The plugin automatically adds required settings, such as the Android backup exclusion rules, to ensure data integrity.
* Eliminating Manual File Edits: Direct modification of native files like AndroidManifest.xml (for Android) or Info.plist (for iOS) is not necessary.
## SDK Initialization Configuration (XML and Plist files)
As specified in the configFilePath property, your core MoEngage credentials and SDK settings do not go in app.json. Instead, they are defined in separate files for each platform.
* For Android: [Generate](https://app-cdn.moengage.com/sdk/integration/config/index.html) an XML file (e.g., android\_initialisation\_config.xml).
* For iOS: [Generate](https://app-cdn.moengage.com/sdk/integration/config/index.html) a Plist file (e.g., MoEngage-Config.plist).
The following tables detail the available properties for these files.
**Android SDK Configuration**
| Section | Field Name | Description |
| :-------------------- | :------------------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Core** | Moengage Workspace ID | This field denotes the unique identifier that links your application to a specific workspace within your MoEngage dashboard. |
| Core | Moengage Project ID | This field denotes the unique identifier associated with a specific project if you have [*Portfolio*](https://www.moengage.com/docs/user-guide/settings/account/portfolio/portfolio) feature enabled for your workspace. |
| Core | Moengage Data Center | This field denotes the data center based on the dashboard URL. [Refer here](/docs/developer-guide/android-sdk/sdk-integration/basic-integration/data-center) for more details. |
| Core | MoengageEnvironment | This field denotes the environment for data reporting, such as LIVE for the live application or TEST for development, to prevent test data from affecting production analytics. |
| Core | Custom Proxy Domain | This field denotes the base custom proxy domain used to route SDK network traffic through your own subdomain. (Key: `com_moengage_core_custom_base_domain`) |
| Core | Enable Logging for Release build | This field denotes whether to activate SDK logging in the production version of your app. It is recommended to disable this in release builds to improve performance and prevent exposure of sensitive data. |
| Core | Log Level | This field denotes the verbosity of the logs generated by the SDK, typically ranging from detailed (VERBOSE = 0) to minimal (ERROR = 3), for debugging purposes. |
| Core | Integration Partner | This field denotes the name of any third-party integration partner you are using, which helps in attributing user acquisition or specific events to that partner. |
| Core | Cache Connection | This field denotes the configuration for how the SDK caches data locally before sending it to MoEngage servers, helping to manage network usage and offline tracking. |
| **Data Tracking** | Track Device Information | This field denotes whether the SDK should track standard device attributes such as the device model, OS version, GAID, and app version. |
| Data Tracking | Enable Carrier Tracking | This field denotes whether the SDK should collect and send the user's mobile carrier information (e.g., Verizon, T-Mobile) for segmentation. |
| Data Tracking | Enable Screen Filtering by Package | This field denotes whether to enable the package-based filtering specified in Screen Tracking Whitelisted Packages. |
| Data Tracking | Screens to Exclude from Tracking | This field denotes a comma-separated list of Android Activity names to be excluded from automatic screen tracking, preventing data collection from sensitive or irrelevant screens. |
| Data Tracking | Enable Background Data Sync | This field denotes whether the SDK is permitted to synchronize data with MoEngage servers while the application is running in the background. |
| Data Tracking | Enable Periodic Data Sync | This field denotes the time interval in milliseconds at which the SDK syncs batched data with MoEngage servers if periodic sync is enabled. |
| **Push Notification** | Push Token Retry Interval(s) | This field denotes the time interval in seconds that the SDK should wait before attempting to resend a push notification that failed to be delivered. |
| Push Notification | Push Notification Small Icon | This field denotes the resource name of the drawable to be used as the small icon for all push notifications from your app. |
| Push Notification | Push Notification Large Icon | This field denotes the resource name of the drawable to be used as the large icon for push notifications. |
| Push Notification | Push Notification Color | This field denotes the hexadecimal color code (e.g., #FFFFFF) used to accent push notifications, affecting elements like the app name and action buttons. |
| Push Notification | Group Multiple Notifications in Drawer | This field denotes the key used to group multiple notifications from your app into a single, stacked notification in the system tray. |
| Push Notification | Enable Push Notification Back Stack Building | This field denotes whether tapping a notification should rebuild the app's task stack, ensuring proper back-button navigation. |
| Push Notification | Enable Notification Large Icon Display | This field denotes whether to show the specified large icon in push notifications. |
| Push Notification | Enable Heads-Up Notification | This field denotes whether to allow high-priority notifications to appear as a floating "heads-up" banner at the top of the screen. |
| Push Notification | Configure FCM Registration | This field denotes the server key from your Firebase Cloud Messaging (FCM) project, which is required to send push notifications to Android devices. |
| Push Notification | Configure Huawei Push Kit Registration | This field denotes the app secret key from your Huawei Mobile Services (HMS) project, required for sending push notifications to Huawei devices. |
| Push Notification | Enable RTT Background Sync | This field denotes whether to enable Real-Time Triggers (RTT) background sync, allowing for more immediate campaign actions based on user behavior. |
| **In-apps** | Screens to Suppress In-Apps | This field denotes a comma-separated list of Android Activity names on which in-app messages should not be displayed. |
| In-apps | Show In-App in New Activity | This field denotes whether in-app messages should be displayed within a new, dedicated Android Activity instead of overlaying the current one. |
| **Cards** | Cards Placeholder Image | This field denotes a URL for a placeholder image that is displayed while the actual Card content is loading. |
| Cards | Cards Empty Inbox Image | This field denotes a URL for an image to be displayed when the user's Card inbox is empty. |
| Cards | Cards Date Format | This field denotes the desired date format (e.g., "dd-MMM-yyyy") for displaying timestamps on Card elements in the UI. |
| Cards | Enable Swipe to Refresh the Cards | This field denotes whether users can pull down on the Card inbox screen to refresh its content. |
| **Security** | Enable Storage Encryption | This field denotes whether to encrypt the MoEngage data that is stored locally on the user's device, enhancing data security. |
| Security | Enable Network Encryption | This field denotes whether to encrypt the data transmitted between the SDK and MoEngage's servers, securing data in transit. |
| Security | Enable JWT Authorisation | This field denotes whether to enable JSON Web Token (JWT) based authorization for authenticating requests from the SDK to the MoEngage backend. |
**iOS SDK Configuration**
| Section | Field Name | Description |
| :---------------- | :--------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Core** | MoEngage Workspace ID | This field denotes the unique identifier that links your application to a specific workspace within your MoEngage dashboard. |
| Core | Moengage Project ID | This field denotes the unique identifier associated with a specific project if you have [Portfolio](https://www.moengage.com/docs/user-guide/settings/account/portfolio/portfolio) feature enabled for your workspace. |
| Core | Moengage Data Center | This field denotes the geographical location of the MoEngage server (e.g., 1 for US, 2 for EU) where your app's data will be stored and processed. |
| Core | MoengageEnvironment | This field denotes the environment for data reporting, such as PROD for the live application or DEV for development, to prevent test data from affecting production analytics. By default, the environment is picked from the build configuration. |
| Core | Custom Proxy Domain | This field denotes the base custom proxy domain used to route SDK network traffic through your own subdomain. (Key: `CustomBaseDomain`) |
| Core | Enable Logging for Release & Debug build | This field denotes whether to activate SDK logging in your app's production version. It is recommended that you disable this in release builds. |
| Core | Integration Partner | This field denotes the name of any third-party integration partner, which helps in attributing user acquisition or specific events to that partner. |
| Core | App Group Name | This field denotes the App Group ID, which allows the main app to share data with its associated extensions, such as Notification Service Extensions. |
| **Data Tracking** | Enable Periodic Data Sync | This field denotes whether to batch and send tracked data to MoEngage at a set interval rather than in real time. Enabling this by default can optimize battery and network usage. |
| **In-apps** | Padding for Inapp | This field denotes the amount of padding (in points) as a safe area to apply around the content of in-app messages to control their spacing and layout. |
| In-apps | Provide Deeplink Call back | This field denotes whether to provide a callback method to the host application for custom handling of deep links triggered from in-app messages. |
| **Security** | Enable Storage Encryption | This field denotes whether to encrypt the MoEngage data that is stored locally on the user's device, enhancing data security. Encryption is not enabled by default. |
| Security | Enable Network Encryption | This field denotes whether to encrypt the data transmitted between the SDK and MoEngage's servers, securing data in transit. Encryption is not enabled by default. |
| Security | JWT Authorization Enabled | This field denotes whether to enable JSON Web Token (JWT) based authorization for authenticating requests from the SDK to the MoEngage backend. |
## Configuration parameters
Add the properties listed below inside the react-native-expo-moengage plugin entry in your app.json or app.config.js file. These parameters control the plugin's build-time behavior, such as linking native modules and pointing to your configuration files.
1. Open your app.json or app.config.js file.
2. Add react-native-expo-moengage to the plugins array.
```json JSON theme={null}
[
"react-native-expo-moengage",
{
"android": {
"configFilePath": "assets/moengage/android_initilisation_config.xml",
"smallIconPath": "assets/moengage/small_icon.png",
"largeIconPath": "assets/moengage/large_icon.png",
"disableMoEngageDefaultBackupFile": false,
"includeFirebaseMessagingDependencies": true,
"isExpoNotificationIntegration": true,
"shouldIncludeMoEngageFirebaseMessagingService": true
}
}
]
```
| Key | Description |
| --------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| configFilePath | Specifies the path to the configuration file (.xml) containing your core MoEngage SDK settings. |
| smallIconPath | Specifies the path to the small icon to be used in push notification. |
| largeIconPath | Specifies the path to the large icon to be used in push notification. |
| disableMoEngageDefaultBackupFile | MoEngage necessitates the exclusion of specific files during backup. This flag is designed to configure this process. If enabled, the configuration will be automatically included; otherwise, you need to add it manually. For more info, refer [here](/docs/developer-guide/android-sdk/sdk-integration/basic-integration/exclude-mo-engage-storage-file-from-auto-backup). |
| includeFirebaseMessagingDependencies | The plugin includes the Firebase Cloud Messaging (FCM) library required for push. |
| isExpoNotificationIntegration | Set to true to ensure compatibility and correctly route push payloads when using the expo-notifications library alongside the MoEngage notification service. |
| shouldIncludeMoEngageFirebaseMessagingService | If enabled, the notification service is included by MoEngage; otherwise, you must pass the payload to the MoEngage SDK. |
```json JSON theme={null}
[
"react-native-expo-moengage",
{
"apple": {
"configFilePath": "assets/moengage/MoEngage-Config.plist",
"pushNotificationImpressionTrackingEnabled": true,
"richPushNotificationEnabled": true,
"pushTemplatesEnabled": true,
"deviceTriggerEnabled": true,
"liveActivityTargetPath": "assets/moengage/LiveActivity"
}
}
],
```
| Key | Description |
| ----------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| configFilePath | Specifies the path to the MoEngage configuration plist file relative to the application project root path. The data in this plist file is added to the MoEngage key in the application's Info.plist. If no path is provided, assets/moengage/MoEngage-Config.plist is assumed to be the path. |
| pushNotificationImpressionTrackingEnabled | Specifies whether to enable push notification delivery impression tracking. The default value is true if not provided. |
| richPushNotificationEnabled | Specifies whether to enable rich media content in push notifications. false if not provided. |
| pushTemplatesEnabled | Whether to enable push notification templates. False if not enabled. For more info, refer [here](/docs/developer-guide/android-sdk/push/optional/push-templates). |
| deviceTriggerEnabled | Whether to enable device-triggered notifications. False if not enabled. For more info, refer [here](/docs/developer-guide/ios-sdk/push/optional/real-time-triggers). |
| liveActivityTargetPath | Specifies the path to the Live Activity widget target files. It should include the widget's UI and payload definitions and any additional resources required by the widget. For more info, refer [here](/docs/developer-guide/ios-sdk/push/optional/broadcast-live-activity). |
## Configure iOS App Extensions with EAS Build
EAS Build automatically handles standard iOS app configurations. However, if your app uses features that require separate native targets, you must configure them in `eas.json`. This includes features like:
* Notification Service Extensions (NSE): used for rich push and impression tracking.
* Notification Content Extensions (NCE): used for Push templates or Live Activities.
To configure iOS app extensions, add the `appExtensions` key to the `expo.extra.eas.build.experimental.ios` object. This configuration ensures EAS Build can correctly compile and sign the additional native targets.
```json JSON theme={null}
{
"expo": {
"extra": {
"eas": {
"build": {
"experimental": {
"ios": {
"appExtensions": [
{
"targetName": "MoEngageExpoRichPush",
"bundleIdentifier": "${YOUR Bundle Identifier}.MoEngageExpoRichPush",
"entitlements": {
"com.apple.security.application-groups": ["${YOUR AppGroup provided in apple.configFilePath}"]
}
},
{
"targetName": "MoEngageExpoPushTemplates",
"bundleIdentifier": "${YOUR Bundle Identifier}.MoEngageExpoPushTemplates",
"entitlements": {
"com.apple.security.application-groups": ["${YOUR AppGroup provided in apple.configFilePath}"]
}
},
{
"targetName": "MoEngageExpoLiveActivity",
"bundleIdentifier": "${YOUR Bundle Identifier}",
"entitlements": {
"com.apple.security.application-groups": ["${YOUR AppGroup provided in apple.configFilePath}"]
}
}
]
}
}
}
}
}
}
}
```
## Generate the native project files
After installing the package and adding the plugin entry, you must run the prebuild command. This command uses your configuration to generate the native Android and iOS directories for your project.
Run the following command:
```shell Shell theme={null}
npx expo prebuild
```
## Next steps
After you've added your configuration, the final step is to [initialize](/docs/developer-guide/react-native-sdk/sdk-integration/expo/configure-the-moengage-expo-sdk) the SDK in your app's code.
# Initialization
Source: https://moengage.com/docs/developer-guide/react-native-sdk/sdk-integration/expo/initialization
Initialize the MoEngage SDK in your React Native Expo app to activate tracking and messaging.
## Overview
This is the final step to activate the MoEngage SDK. Calling the initialize method activates your configurations and lets the SDK start tracking data and handling messages.
You must complete the [installation](/docs/developer-guide/react-native-sdk/sdk-integration/expo/installation) and [configuration](/docs/developer-guide/react-native-sdk/sdk-integration/expo/configure-the-moengage-expo-sdk)guides before you initialize the SDK.
## Initialize the SDK
We recommend you initialize the SDK in your app's root component, which is usually App.js or App.tsx. To initialize the SDK, write the following code:
```TypeScript TypeScript theme={null}
import ReactMoE from 'react-native-moengage';
useEffect(() = {
ReactMoE.initialize("YOUR_WORKSPACE_ID");
},[]);
```
The MoEngage SDK is now fully operational in your app.
To see detailed SDK logs in your console for development, initialize with a log configuration. This step is optional.
```TypeScript TypeScript theme={null}
import { MoEInitConfig, MoEPushConfig, MoEngageLogConfig, MoEngageLogLevel } from "react-native-moengage";
const moEInitConfig = new MoEInitConfig(
MoEPushConfig.defaultConfig(),
new MoEngageLogConfig(MoEngageLogLevel.DEBUG, isEnabledForReleaseBuild)
);
ReactMoE.initialize(YOUR_WORKSPACE_ID, moEInitConfig);
```
The minimum supported version for `expo-notification` is *0.31.0*.
## Next steps
With the SDK initialized, you can start using other MoEngage features:
* [Track user events and attributes](/docs/developer-guide/react-native-sdk/data-tracking/tracking-user-attributes-and-user-identity)
* [Set up push notifications](/docs/developer-guide/react-native-sdk/push/basic/android-push-configuration)
* [Implement in-app messages](/docs/developer-guide/react-native-sdk/in-app-messages/inapp-nativ)
# Installation
Source: https://moengage.com/docs/developer-guide/react-native-sdk/sdk-integration/expo/installation
Add the MoEngage SDK to your React Native Expo project using the npx expo install command.
## Overview
This guide shows you how to add the MoEngage SDK to your React Native Expo project.
**Prerequisites**
Make sure you have the following:
* A MoEngage account.
* A React Native project set up with Expo.
## Install the SDK Package
You'll add the MoEngage SDK to your project as a package. We recommend using `npx expo install` because it automatically installs a version that's compatible with your project's Expo SDK.
Open your terminal and go to your project's root directory.
Run the following commands:
```shell Shell theme={null}
npx expo install react-native-expo-moengage
```
```shell Shell theme={null}
npm install react-native-moengage
```
## Add the plugin to your configuration
Next, you need to add the MoEngage plugin to your Expo configuration file (`app.json` or `app.config.js`).
This step is required before generating the native project files.
```json theme={null}
{
"expo": {
"plugins": [
"react-native-expo-moengage"
]
}
}
```
## Next steps
The SDK is now installed. The next step is to [configure](/docs/developer-guide/react-native-sdk/sdk-integration/expo/configure-the-moengage-expo-sdk) it with your app-specific details.
# Limitations
Source: https://moengage.com/docs/developer-guide/react-native-sdk/sdk-integration/limitations
Review features not supported or requiring native implementation in the MoEngage React Native plugin.
Compared to the Native Android or iOS SDKs there are a certain set of features we either do not support or require native Android or iOS implementation when using our React-Native plugin.
# Features not supported
* Action Buttons in iOS Notifications
# File Based Initialization
Source: https://moengage.com/docs/developer-guide/react-native-sdk/sdk-integration/react-native/sdk-initialization/file-based-initialization/file-based-initialization
Use file-based initialization to configure MoEngage React Native SDK with native config files.
## Overview
Starting with v12.0.0, the React Native SDK supports file-based initialization.
To streamline the integration process and minimize initialization errors, MoEngage supports Script-Based Initialization. This approach allows you to manage App IDs and configuration settings directly within native configuration files, keeping them separate from your application logic.
This article outlines how you can use the form-based interface to generate a validated code snippet for initialization and access module-specific configurations.
Alternatively, the SDK can be initialized manually. If you require this approach, please refer to the guide on [Framework Initialization](/docs/developer-guide/react-native-sdk/sdk-integration/react-native/sdk-initialization/manual-initialization/framework-initialization).
## Script-based Initialization
Follow these steps to generate your initialization script:
Navigate to the [Initialization Website.](https://app-cdn.moengage.com/sdk/integration/config/index.html)
Configure the values based on your application requirements. Refer to the Configuration Parameters tables below for:
* [Android](/docs/developer-guide/react-native-sdk/sdk-integration/react-native/sdk-initialization/file-based-initialization/file-based-initialization#android-configuration-reference)
* [iOS](/docs/developer-guide/react-native-sdk/sdk-integration/react-native/sdk-initialization/file-based-initialization/file-based-initialization#ios-configuration-reference)
Click **Generate Code** at the bottom of the form.
## Android Configuration (XML)
For Android, initialization is handled by placing an XML configuration file in the application's resource directory.
### Android Configuration Reference
Below is the comprehensive list of keys available for `moengage.xml`.
| Category | XML Key Name | Type | Description |
| :----------- | :---------------------------------------------------- | :------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Core** | `com_moengage_core_workspace_id` | String | Specifies your App ID. This field is mandatory. |
| | `com_moengage_core_file_based_initialisation_enabled` | Boolean | Set to `true` to enable this feature. |
| | `com_moengage_core_data_center` | Integer | Default: `1`. For more info, refer to [Data Center values](/docs/developer-guide/react-native-sdk/sdk-integration/react-native/sdk-initialization/file-based-initialization/file-based-initialization#data-center-values). |
| | `com_moengage_core_environment` | String | Supported values: `default`, `live`, or `test`. |
| | `com_moengage_core_custom_base_domain` | String | Specifies the base custom proxy domain to route SDK network traffic through your own subdomain. |
| | `com_moengage_core_integration_partner` | String | Specifies the core integration partner (e.g., `segment` or `mparticle`). |
| **Push** | `com_moengage_push_notification_small_icon` | Drawable | Resource ID for small icon. |
| | `com_moengage_push_notification_large_icon` | Drawable | Resource ID for large icon. |
| | `com_moengage_push_notification_color` | Color | Notification accent color. |
| | `com_moengage_push_notification_token_retry_interval` | Integer | Retry interval (in seconds) for token registration. |
| | `com_moengage_push_kit_registration_enabled` | Boolean | If `true`, SDK registers for push token. |
| **Logs** | `com_moengage_core_log_level` | Integer | `0` (No Log) to `5` (Verbose). Default: `3`. |
| | `com_moengage_core_log_enabled_for_release_build` | Boolean | If `true`, prints logs in release builds. |
| **Security** | `com_moengage_core_storage_encryption_enabled` | Boolean | Enables local storage encryption. |
| | `com_moengage_core_network_encryption_enabled` | Boolean | Enables payload encryption over the network. |
| **Sync** | `com_moengage_core_periodic_data_sync_enabled` | Boolean | Enables periodic data sync in the foreground. |
| | `com_moengage_core_background_data_sync_enabled` | Boolean | Enables periodic data sync in the background. |
| **In-App** | `com_moengage_inapp_show_in_new_activity_enabled` | Boolean | Required for specific TV/Android setups. |
**Troubleshooting**
If the XML file is missing or the `com_moengage_core_workspace_id` is empty, the SDK will throw a `ConfigurationMismatchError`.
### Add Configuration File
Place the generated file in `android/app/src/main/res/values/`.
## iOS Configuration (Info.plist)
For iOS, initialization is handled by adding a configuration dictionary to your `Info.plist`.
### iOS Configuration Reference
Below is the comprehensive list of keys available for the `MoEngage` dictionary.
| Category | Plist Key | Type | Description |
| :----------- | :----------------------------------- | :--------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Core** | `WorkspaceId` | String | Specifies your App ID. Mandatory. |
| | `IsSdkAutoInitialisationEnabled` | Boolean | Set to `true` to enable SDK auto-initialisation. |
| | `DataCenter` | Integer | Mandatory. Default: `1`. Refer to [Data Center values](/docs/developer-guide/flutter-sdk/sdk-integration/sdk-initialization/file-based-initialization/file-based-initialization#data-center-values). |
| | `IsTestEnvironment` | String / Boolean | Default: `$(SWIFT_ACTIVE_COMPILATION_CONDITIONS)`. |
| | `CustomBaseDomain` | String | Specifies the custom proxy domain subdomain. |
| | `IntegrationPartner` | String | Integration partner (e.g., `segment` or `mparticle`). |
| | `AppGroupName` | String | App Group name for sharing SDK data. |
| **Logs** | `IsLoggingEnabled` | Boolean | Set to `true` to enable SDK logs. |
| | `Loglevel` | Integer | `0` to `5`. Default: `2`. |
| **Security** | `IsStorageEncryptionEnabled` | Boolean | Enables local storage encryption. |
| | `KeychainGroupName` | String | Mandatory if storage encryption is enabled. |
| | `IsNetworkEncryptionEnabled` | Boolean | Enables payload encryption. |
| | `EncryptionEncodedTestKey` | String | Dashboard auto-populated string for Test environment. |
| | `EncryptionEncodedLiveKey` | String | Dashboard auto-populated string for Live environment. |
| **Sync** | `AnalyticsEnablePeriodicFlush` | Boolean | Enables periodic data flush. Default: `true`. |
| | `AnalyticsPeriodicFlushDuration` | Integer | Flush interval in seconds. Default: `60`. |
| **In-App** | `InAppDisplaySafeAreaInset` | Real | Safe area padding padding value. |
| | `InAppShouldProvideDeeplinkCallback` | Boolean | Provides callback on deeplink if `true`. |
### Data Center Values
Configure the integer corresponding to your region. Incorrect values will result in data loss.
| Data Center | Dashboard host |
| :---------- | :------------------------ |
| 1 | dashboard-01.moengage.com |
| 2 | dashboard-02.moengage.com |
| 3 | dashboard-03.moengage.com |
| 4 | dashboard-04.moengage.com |
| 5 | dashboard-05.moengage.com |
| 6 | dashboard-06.moengage.com |
### Update Info.plist
Open your project's `Info.plist` (found in `ios/ProjectName/`).
Create a new Top-Level Key named `MoEngage` of type `Dictionary`.
Add the configuration file content generated in the [Initialization Website.](https://app-cdn.moengage.com/sdk/integration/config/index.html)
**Warning** The key `IsSdkAutoInitialisationEnabled` uses the British spelling ('s'). Ensure you use the exact key name, or initialization will fail.
**XML Snippet Representation:**
```xml theme={null}
MoEngageWorkspaceIdYOUR_WORKSPACE_IDIsTestEnvironment$(SWIFT_ACTIVE_COMPILATION_CONDITIONS)|$(GCC_PREPROCESSOR_DEFINITIONS)DataCenter1CustomBaseDomaindata.example.comIsLoggingEnabled
```
## Framework Level Initialization
After you configure the native files, the initialization code in your hybrid framework is simplified.
### Android Native Setup
Add the following code to `android/app/src/main/java/com/your_app/MainApplication.java` (or `.kt`) inside the `onCreate()` method.
```kotlin theme={null}
import com.moengage.react.MoEInitializer
override fun onCreate() {
super.onCreate()
MoEInitializer.initializeDefaultInstance(application)
}
```
```java theme={null}
import com.moengage.react.MoEInitializer;
@Override
public void onCreate() {
super.onCreate();
// ... existing code
MoEInitializer.INSTANCE.initializeDefaultInstance(this);
}
```
### iOS Native Setup
Add the following code to your `AppDelegate` class inside the `didFinishLaunchingWithOptions` method.
```swift theme={null}
import MoEngageSDK
override func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool {
// ... existing code
MoEngage.sharedInstance.initializeDefaultInstance()
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
}
```
```objectivec theme={null}
#import
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
// ... existing code
[[MoEngageInitializer sharedInstance] initializeDefaultInstanceWithAdditionalReactConfig:[[MoEngageReactSDKDefaultInitializationConfig alloc] init]];
return [super application:application didFinishLaunchingWithOptions:launchOptions];
}
```
### Initialize React-Native Component
Initialize the MoEngage Plugin in the ***App.js*** or ***App.ts*** of your application once the component is mounted.
```typescript theme={null}
import ReactMoE from 'react-native-moengage';
ReactMoE.initialize("YOUR_WORKSPACE_ID");
```
If you have a class-based component, you can initialize in the `render()` or `componentDidMount()`.
## Migration and Precedence
To migrate from manual code-based initialization to file-based approach, refer [here](/docs/developer-guide/react-native-sdk/sdk-integration/react-native/sdk-initialization/file-based-initialization/migration-and-precedence).
## Environments (Test vs. Live)
You can configure Test/Live environments within these files:
* **Android:** Use `test`.
* **iOS:** Use `IsTestEnvironment`.
# Migration and Precedence
Source: https://moengage.com/docs/developer-guide/react-native-sdk/sdk-integration/react-native/sdk-initialization/file-based-initialization/migration-and-precedence
Migrate your MoEngage React Native SDK from manual initialization to file-based configuration.
### Android Migration Steps
1. **Add Configuration File:** Place the `moengage.xml` file in `src/main/res/values/moengage.xml`.
2. **Update Application Class:** Remove manual `MoEngage.Builder` logic and replace it with `initializeDefaultInstance`.
```java theme={null}
import com.moengage.react.MoEInitializer;
MoEInitializer.INSTANCE.initializeDefaultInstance(this);
```
```kotlin theme={null}
import com.moengage.react.MoEInitializer
MoEInitializer.initializeDefaultInstance(application)
```
### iOS Migration Steps
1. **Update Info.plist**: Add WorkspaceId, DataCenter, etc., directly to your `Info.plist` file.
2. **Update AppDelegate**: Remove manual `MoEngageSDKConfig` logic and call `initializeDefaultInstance`.
```objectivec theme={null}
#import
[[MoEngageInitializer sharedInstance] initializeDefaultInstanceWithAdditionalReactConfig:[[MoEngageReactSDKDefaultInitializationConfig alloc] init]];
```
```swift theme={null}
import MoEngageSDK
MoEngage.sharedInstance.initializeDefaultInstance()
```
### Precedence Rules
The source of configuration is determined by the initialization function called in your native code:
* **Android**:
* **File-Based Init:** `initializeDefaultInstance(context)` reads `moengage.xml`.
* **Code-Based Init:** `initialize(context, moEngage.Builder)` uses the configuration object and ignores XML.
* **iOS:** Auto-initialization via `Info.plist` occurs first. Calling the manual `initialize` method with a configuration object later will update the current instance.
# Android
Source: https://moengage.com/docs/developer-guide/react-native-sdk/sdk-integration/react-native/sdk-initialization/manual-initialization/android
Initialize the MoEngage SDK in your Android Application class for React Native integration.
# SDK Initialization
Get the Workspace ID from the Settings Page \_Dashboard --> Settings --> App --> Genera\_l on the MoEngage dashboard and initialize the MoEngage SDK in the ***Application*** class's ***onCreate()***.
It is recommended that you initialize the SDK on the main thread inside ***onCreate()*** and not create a worker thread and initialize the SDK on the worker thread.
```kotlin Kotlin theme={null}
import com.moengage.core.DataCenter
import com.moengage.core.MoEngage
import com.moengage.react.MoEInitializer
val moEngage = MoEngage.Builder(this, "YOUR_WORKSPACE_ID", DataCenter.DATA_CENTER_X)
//replace X with your data center number
MoEInitializer.initializeDefaultInstance(applicationContext, moEngage)
```
```java Java theme={null}
import com.moengage.core.DataCenter;
import com.moengage.core.MoEngage;
import com.moengage.react.MoEInitializer;
MoEngage.Builder moEngage = new MoEngage.Builder(this, "YOUR_WORKSPACE_ID", [YOUR_DATA_CENTER]);
MoEInitializer.INSTANCE.initializeDefaultInstance(getApplicationContext(), moEngage);
```
Following details of the different data centers you need to set based on the dashboard hosts
| Data Center | Dashboard host |
| -------------------------- | ------------------------- |
| DataCenter.DATA\_CENTER\_1 | dashboard-01.moengage.com |
| DataCenter.DATA\_CENTER\_2 | dashboard-02.moengage.com |
| DataCenter.DATA\_CENTER\_3 | dashboard-03.moengage.com |
| DataCenter.DATA\_CENTER\_4 | dashboard-04.moengage.com |
| DataCenter.DATA\_CENTER\_5 | dashboard-05.moengage.com |
| DataCenter.DATA\_CENTER\_6 | dashboard-06.moengage.com |
Refer to the [API reference doc](https://moengage.github.io/android-api-reference/core/com.moengage.core/-mo-engage/-builder/index.html) for a detailed list of possible configurations.
# Exclude MoEngage Storage File from Auto-Backup
This is a mandatory integration step and is very essential to prevent your data from getting corrupted. Android's auto back-up service periodically backs up Shared Preference files, Database files, etc, more details [here](https://developer.android.com/guide/topics/data/autobackup). This backup results in MoEngage SDK's identifiers being backed up and restored after re-install.This restoration of the identifier results in your data being corrupted and users not being reachable via push notifications.
To ensure data is not corrupted after a backup is restored, opt out of MoEngage SDK's storage files. Refer to [Exclude MoEngage Storage File from the Auto-Backup](/docs/developer-guide/android-sdk/sdk-integration/basic-integration/exclude-mo-engage-storage-file-from-auto-backup) section of the documentation to learn more about which files to exclude.
# Framework Initialization
Source: https://moengage.com/docs/developer-guide/react-native-sdk/sdk-integration/react-native/sdk-initialization/manual-initialization/framework-initialization
Initialize the MoEngage React Native plugin in your App.js or App.ts after the component mounts.
# Initialize React-Native Component
Initialize the MoEngage Plugin in the **App.js** or ***App***\*.\*\*\****ts*** of your application once the component is mounted.
```java TypeScript theme={null}
import ReactMoE from 'react-native-moengage';
ReactMoE.initialize("YOUR_WORKSPACE_ID");
```
**Example**
```java TypeScript theme={null}
import ReactMoE from 'react-native-moengage';
useEffect(() => {
ReactMoE.initialize("YOUR_WORKSPACE_ID");
}, []);
```
If you have a class-based component then you can initialize in the ***render()*** or ***componentDidMount()***
## Initialize with Configuration (optional)
```java TypeScript theme={null}
import ReactMoE from 'react-native-moengage';
import { MoEInitConfig, MoEPushConfig, MoEngageLogConfig, MoEngageLogLevel } from "react-native-moengage";
const moEInitConfig = new MoEInitConfig(
MoEPushConfig.defaultConfig(),
new MoEngageLogConfig(MoEngageLogLevel.DEBUG, isEnabledForReleaseBuild)
);
ReactMoE.initialize(YOUR_WORKSPACE_ID, moEInitConfig);
```
Make sure you are setting the Push/InApp callback listeners before calling the ***initialize()***.
Refer to the following for platform-specific initialization:
* [Android](/docs/developer-guide/react-native-sdk/sdk-integration/react-native/sdk-initialization/manual-initialization/android)
* [iOS](/docs/developer-guide/react-native-sdk/sdk-integration/react-native/sdk-initialization/manual-initialization/ios)
For more information about samples, refer to [React-Native Sample App](https://github.com/moengage/React-Native/).
# iOS
Source: https://moengage.com/docs/developer-guide/react-native-sdk/sdk-integration/react-native/sdk-initialization/manual-initialization/ios
Initialize the MoEngage SDK in your iOS AppDelegate for React Native integration.
For initializing the project, you'll need to provide the Workspace ID of your MoEngage App.
Login to your MoEngage account, go to Settings in the left panel of the dashboard. Under App Settings, you will find your Workspace ID.
## Code Initialisation
To initialize MoEngageSDK from ***application:didfinishlaunchingwithoptions*** call any one of the below initialization methods by passing MoEngageSDKConfig as parameter. Refer [doc](/docs/developer-guide/ios-sdk/sdk-integration/basic/sdk-initialization) for more info on all the properties that can be configured using ***MoEngageSDKConfig***
Objective-C
```objectivec Objective-C theme={null}
/// @param sdkConfig MoEngageSDKConfig instance for SDK configuration
/// @param launchOptions Launch Options dictionary
- (void)initializeDefaultSDKConfig:(MoEngageSDKConfig*)sdkConfig andLaunchOptions:(NSDictionary*)launchOptions;
/// @param sdkConfig MoEngageSDKConfig instance for SDK configuration
/// @param isSdkEnabled Bool indicating if SDK is Enabled/Disabled
/// @param launchOptions Launch Options dictionary
- (void)initializeDefaultSDKConfigWithState:(MoEngageSDKConfig*)sdkConfig withSDKState:(MoEngageSDKState)sdkState andLaunchOptions:(NSDictionary*)launchOptions;
```
Sample code to initialize from ***application:didFinishLaunchingWithOptions:*** method:
```swift Swift theme={null}
import MoEngageSDK
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
//Add your MoEngage Workspace ID and Data center.
let sdkConfig = MoEngageSDKConfig(appId: "YOUR_WORKSPACE_ID", dataCenter: MoEngageDataCenter.data_center_0x) sdkConfig.consoleLogConfig = MoEngageConsoleLogConfig(isLoggingEnabled: true, loglevel: .verbose)
MoEngageInitializer.sharedInstance().initializeDefaultSDKConfig(sdkConfig, andLaunchOptions: launchOptions)
//Rest of the implementation of method
//-------
return true
}
```
```objectivec Objective-C theme={null}
#import
#import @implementation AppDelegate
- (BOOL)application:(UIApplication*)application didFinishLaunchingWithOptions:(NSDictionary*)launchOptions
{
MoEngageSDKConfig* sdkConfig = [[MoEngageSDKConfig alloc] initWithAppId:@"YOUR_WORKSPACE_ID" dataCenter: MoEngageDataCenterData_center_0x];
sdkConfig.consoleLogConfig = [[MoEngageConsoleLogConfig alloc] initWithIsLoggingEnabled:true loglevel:MoEngageLoggerTypeVerbose];
[[MoEngageInitializer sharedInstance] initializeDefaultSDKConfig:sdkConfig andLaunchOptions:launchOptions];
return YES;
}
```
# Data Center
In case your app wants to redirect data to a specific zone due to any data regulation policy please configure the zone in the MOSDKConfig object.
Refer to the Data Center documentation for more information.
* [Android](/docs/developer-guide/android-sdk/sdk-integration/basic-integration/data-center)
* [iOS](/docs/developer-guide/ios-sdk/sdk-integration/basic/data-center)
# Android Build Configuration
Source: https://moengage.com/docs/developer-guide/react-native-sdk/sdk-integration/react-native/sdk-installation/android
Configure Android build settings and add required dependencies for the MoEngage React Native SDK.
## Prerequisites
Before you continue, make sure you have:
* Installed the [`react-native-moengage`](/docs/developer-guide/react-native-sdk/sdk-integration/react-native/sdk-installation/framework-dependency) package in your project.
* React Native `0.60` or later. Older versions do not support autolinking and require manual native linking.
* Android `compileSdkVersion` `34` or later in `android/build.gradle`.
## Configuring Build Settings
The MoEngage SDK depends on the `lifecycle-process` library, which in turn depends on `androidx.startup:startup-runtime`. To keep SDK features working:
* Do not remove the `InitializationProvider` component from your `AndroidManifest.xml`.
* If you add other initializers that use `startup-runtime`, also add the initializer for `lifecycle-process`.
* See the [Lifecycle 2.4.0 release notes](https://developer.android.com/jetpack/androidx/releases/lifecycle#2.4.0) for instructions on adding the initializer.
## Add AndroidX Libraries
The SDK depends on a few AndroidX libraries for its functioning. Choose one of the following approaches to add them — either enable the flag in `package.json` (recommended) or add the dependencies directly to your `build.gradle` file.
### Configure in the package.json File
* Setting `includeAndroidXRequiredLibraries` to `true` automatically adds the AndroidX libraries that the SDK uses as dependencies.
* **Required AndroidX libraries:** The MoEngage SDK requires the following core AndroidX dependencies:
* `androidx.core:core`
* `androidx.appcompat:appcompat`
* `androidx.lifecycle:lifecycle-process`
* Before enabling this flag, check whether your application's native Android project (`build.gradle`) already includes compatible versions of these libraries. If it does, you may not need to pull them in again, which helps prevent version conflicts or duplicate dependency errors during the build process.
File — `package.json`
```json JSON theme={null}
{
"moengage": {
"includeAndroidXRequiredLibraries": true
}
}
```
### Configure in the Gradle File
Skip this section if you set `includeAndroidXRequiredLibraries: true` in `package.json` above.
Path — `android/app/build.gradle` (use `build.gradle` for Groovy or `build.gradle.kts` for Kotlin build scripts).
```groovy Groovy theme={null}
dependencies {
...
implementation("androidx.core:core:1.9.0")
implementation("androidx.appcompat:appcompat:1.4.2")
implementation("androidx.lifecycle:lifecycle-process:2.7.0")
}
```
```kotlin Kotlin theme={null}
dependencies {
...
implementation("androidx.core:core:1.9.0")
implementation("androidx.appcompat:appcompat:1.4.2")
implementation("androidx.lifecycle:lifecycle-process:2.7.0")
}
```
Use the versions shown or higher. Check [Google Maven](https://maven.google.com) for the latest stable releases.
## Verify the Build
After adding the dependencies, run the following command from your project root to confirm that the MoEngage and AndroidX dependencies are resolved correctly:
```bash Bash theme={null}
cd android && ./gradlew :app:dependencies | grep moengage
```
If the command returns one or more MoEngage entries, the dependencies are linked.
## Feature Modules (Optional)
To include optional modules from the MoEngage SDK based on your feature requirements, use the provided flags. By default, these modules are not included in your project.
Configure feature modules by adding a `moengage` key to the root of your project's `package.json` file. Set a flag to `true` to download and link the native dependencies for that module; omit it or set it to `false` to exclude the module.
File — `package.json`
```json JSON theme={null}
{
"moengage": {
"richNotification": true,
"encryption": true,
"pushAmp": true,
"hmsPushkit": true,
"deviceTrigger": true
}
}
```
The following table describes each flag:
| Flag | Enables |
| ------------------ | -------------------------- |
| `richNotification` | Push Templates |
| `encryption` | Add-On Security |
| `pushAmp` | Push Amplification |
| `hmsPushkit` | HMS PushKit |
| `deviceTrigger` | Device Triggered campaigns |
### Documentation for Optional Feature Flags
The following links cover the Android native SDK. The behavior is equivalent in the React Native SDK.
* [Push Templates](/docs/developer-guide/android-sdk/push/optional/push-templates)
* [Add-On Security](/docs/developer-guide/android-sdk/sdk-integration/advanced-or-optional/add-on-security)
* [Push Amplification](/docs/developer-guide/android-sdk/push/optional/push-amplification)
* [HMS PushKit](/docs/developer-guide/android-sdk/push/optional/push-amp-plus/configuring-hms-push-kit)
* [Device Triggered](/docs/developer-guide/android-sdk/push/optional/device-triggered)
# Framework Dependency
Source: https://moengage.com/docs/developer-guide/react-native-sdk/sdk-integration/react-native/sdk-installation/framework-dependency
Install the MoEngage React Native plugin using npm and link your native dependencies.
Connect your IDE to the [MoEngage docs MCP server](/docs/developer-guide/connect-your-ide-to-moengage-docs) for accurate, context-aware SDK guidance inside your development environment. Your assistant searches this documentation directly, helping you move through the integration with confidence.
Install the MoEngage React Native plugin using the npm package manager.
Link your native dependencies using the following code:
```shell Shell theme={null}
$ npm install react-native-moengage
# required only if you are using versions that do not support auto linking
# This command is removed in version 0.69 of react-native
$ react-native link react-native-moengage
```
A working Sample App can be found [here](https://github.com/moengage/React-Native/tree/master/SampleApp).
After installing the plugin use the following platform-specific configuration.
* [Android](/docs/developer-guide/react-native-sdk/sdk-integration/react-native/sdk-installation/android)
* [iOS](/docs/developer-guide/react-native-sdk/sdk-integration/react-native/sdk-installation/ios)
# iOS
Source: https://moengage.com/docs/developer-guide/react-native-sdk/sdk-integration/react-native/sdk-installation/ios
Set up iOS dependencies for the MoEngage React Native SDK with Turbo architecture support.
Due to the recent [CocoaPods Specs Repo deprecation](https://blog.cocoapods.org/CocoaPods-Specs-Repo/), you must explicitly define your dependency sources to ensure your project builds successfully.
Please add the following lines at the very top of your `Podfile`, above any `target` blocks:
```ruby Ruby theme={null}
source 'https://github.com/moengage/PodSpecs.git'
source 'https://github.com/CocoaPods/Specs.git'
```
MoEngage Source is required to successfully resolve and fetch MoEngage-specific SDK pods.
CocoaPods Source is required to ensure all your other standard third-party React Native dependencies continue to resolve correctly.
After adding these lines to your `Podfile`, run the following command from your ios directory:
```bash Bash theme={null}
pod install --repo-update
```
We now offer support for turbo architecture starting from version 10.0.0.
To run the application in the new architecture, follow these steps:
1. Navigate to the iOS folder.
2. Run the command ***RCT\_NEW\_ARCH\_ENABLED=1 bundle exec pod install*** to install the necessary dependencies.
To run the application in the old react architecture, follow these steps:
1. Navigate to the iOS folder.
2. Run the command ***pod install*** to install the necessary dependencies
# Troubleshooting and FAQs - React Native
Source: https://moengage.com/docs/developer-guide/react-native-sdk/troubleshooting-and-faqs/troubleshooting-and-faqs-react
Find answers to common MoEngage React Native SDK issues with push, callbacks, in-app messages, and iOS build configuration.
# Android - Why are notifications not working in the background or killed state?
Ensure that the MoEngage SDK is initialised in the main thread in the Android Native application class.
Sample code for initialisation - [GitHub](https://github.com/moengage/React-Native/blob/master/SampleApp/android/app/src/main/java/com/moengage/sampleapp/MainApplication.java)
# Android - Why are callbacks not working in the background or killed state?
MoEngage callbacks must be registered in your app.js or app.ts, and after setting them up, you must call the MoEngage Plugin's initialize () method. Read more about [it here](/docs/developer-guide/react-native-sdk/push/basic/push-callback#push-click-callback)
Sample code for callbacks - [GitHub](https://github.com/moengage/React-Native/blob/master/SampleApp/App.js)
# Android - Why are inapp/nudge deep links not working?
MoEngage SDK doesn't handle in-app redirections by default except for rich landing pages; please refer to the documentation here. You must implement in-app click callback methods in your app.ts or app.js and call the moengage plugin initialise() method after you register for callbacks. In these callbacks, you will have to write code to extract navigation information and handle the redirection according to your preference. Callback documentation is [given here](/docs/developer-guide/react-native-sdk/in-app-messages/inapp-nativ#inapp-callbacks).
Sample code for inapp/nudge callbacks - [GitHub](https://github.com/moengage/React-Native/blob/master/SampleApp/App.js)
# Android - Why are inapp/nudge callbacks not working?
Refer to [this documentation](/docs/developer-guide/react-native-sdk/in-app-messages/inapp-nativ#inapp-callbacks) to set up inapp callbacks. Additionally, you must register the callbacks in your application's app.js or app.ts, and after setting them up, you must call the MoEngage Plugin's initialize () method.
Sample code for inapp/nudge callbacks - [GitHub](https://github.com/moengage/React-Native/blob/master/SampleApp/App.js)
# Android - What is MoEDebuggerActivity?
The MoEngage SDK bundles the native MoEngage Android SDK, so your Android build includes `MoEDebuggerActivity`, a component that supports on-device SDK debugging. Refer to [What is MoEDebuggerActivity?](/docs/developer-guide/android-sdk/troubleshooting-and-faqs/troubleshooting-and-faqs) to understand what it does.
To remove it from your app, add the following to your Android project's `AndroidManifest.xml`:
```xml theme={null}
```
# iOS - Why does the build fail with "Unsupported Swift Architecture" or "framework not found"?
Errors such as `Unsupported Swift Architecture`, `framework 'MoEngageKMMConditionEvaluator' not found`, and `framework 'MoEngageRichNotification' not found` usually mean that your Xcode project excludes the `arm64` architecture or builds for architectures that the MoEngage iOS SDK doesn't ship.
First, check the `post_install` hook in your `Podfile` for lines that set `EXCLUDED_ARCHS` or `ONLY_ACTIVE_ARCH`. This hook runs on every `pod install` and overrides the values you set in Xcode. To apply the correct settings to all Pods targets, use the following hook:
```ruby Podfile theme={null}
post_install do |installer|
installer.pods_project.targets.each do |target|
target.build_configurations.each do |config|
config.build_settings['ONLY_ACTIVE_ARCH'] = 'YES'
config.build_settings['EXCLUDED_ARCHS[sdk=iphonesimulator*]'] = ''
end
end
end
```
Next, open `ios/YourApp.xcworkspace` in Xcode, not the `.xcodeproj`. In the **Project Navigator**, select your app project and apply the following settings to every target, including the app target and the notification service and content extensions. Then select the **Pods** project and repeat these settings for its targets:
* **Excluded Architectures (`EXCLUDED_ARCHS`)**: Remove `arm64` from this list. iPhone and iPad devices and Apple silicon simulators require `arm64`, and MoEngage iOS SDK v10.x.x and above ship only `arm64` slices.
* **Build Active Architecture Only (`ONLY_ACTIVE_ARCH`)**: Set this to **Yes** so that Xcode builds only for the architecture of the selected device or simulator.
* **Architectures (`ARCHS`)**: Set this to `ARCHS_STANDARD`, or add `arm64` to the list.
After you change these settings, select **Product > Clean Build Folder** in Xcode, run `pod install` from the `ios` directory, and build again.
Don't set `MoEngageKMMConditionEvaluator` to **Do Not Embed**. This clears the framework-not-found error, but the framework evaluates trigger conditions for trigger-based in-app messages and push campaigns. Without it, those campaigns aren't displayed.
If the build fails on `arm64` in your CI pipeline but succeeds in Xcode on your machine, check the Xcode version on the build agent. Build agents often run an older version. Update the agent to a current stable version of Xcode, and at minimum to the version you build with locally.
Refer to [Configuring Project for Architecture Compatibility](/docs/developer-guide/ios-sdk/sdk-integration/basic/Configuring-Project-for-Architecture-Compatibility) for the full architecture configuration procedure, including Intel-based Mac limitations.
# iOS - Why does the build fail with "Use of undeclared identifier 'MoEngageInitializer'"?
This error occurs when the MoEngage import in `ios/YourApp/AppDelegate.m` or `AppDelegate.mm` is inside or after the `#if RCT_NEW_ARCH_ENABLED` block. If your app doesn't use the React Native new architecture, this condition is false and the compiler skips the import, so `MoEngageInitializer` is never declared.
Move the import above the `#if RCT_NEW_ARCH_ENABLED` block:
```objectivec AppDelegate.mm theme={null}
#import "AppDelegate.h"
#import
#import
#if RCT_NEW_ARCH_ENABLED
// React Native new architecture imports
#endif
```
# iOS - Why is the ReactNativeMoEngage header not found after a plugin upgrade?
Errors such as `'ReactNativeMoEngage/MoEngageInitializer.h' file not found` after a plugin upgrade have two common causes:
* **The header name changed across plugin versions.** Older plugin versions use `MOReactInitializer.h` and current versions use `MoEngageInitializer.h`. Update the import in `ios/YourApp/AppDelegate.m` or `AppDelegate.mm` to match the version you installed. Refer to [iOS initialization](/docs/developer-guide/react-native-sdk/sdk-integration/react-native/sdk-initialization/manual-initialization/ios) for the current import and initialization code.
* **The target can't resolve the header path.** In **Build Settings** for the `ReactNativeMoEngage` target, check that `$(inherited)` is present in both **Header Search Paths** and **Library Search Paths** so that the target picks up the paths generated by CocoaPods. Also check that the `ReactNativeMoEngage` pod is listed in the `Pods` project after you run `pod install`. If the pod is missing, the install didn't link the plugin.
To re-link the plugin after upgrading, run the following commands from your project root:
```shellscript Shell theme={null}
rm -rf ios/Pods ios/Podfile.lock
npm install
cd ios && pod install --repo-update
```
Then clean the build folder in Xcode and build again.
# TV Support
Source: https://moengage.com/docs/developer-guide/react-native-sdk/tv/tv-support
Learn about MoEngage SDK support for Android TV and Apple TV in your React Native applications.
MoEngage supports your apps available on Android TV and Apple TV
Ensure that the [integration](/docs/developer-guide/react-native-sdk/sdk-integration/react-native/sdk-installation/framework-dependency) and [initialization](/docs/developer-guide/react-native-sdk/sdk-integration/react-native/sdk-initialization/manual-initialization/framework-initialization) is completed.
# Supported Features
MoEngage supports the following:
* [Data Tracking](/docs/developer-guide/react-native-sdk/data-tracking/enable-advertising-identifier-tracking)
* [Self-handled InApp](/docs/developer-guide/react-native-sdk/in-app-messages/inapp-nativ)
# Deprecation for Older Platform Versions
Source: https://moengage.com/docs/developer-guide/sdk-lifecycle-and-policies/deprecation-for-older-platform-versions
Review deprecated Android API levels and iOS versions in MoEngage SDK releases after April 2025.
MoEngage will support at minimum API level 23 for Android and iOS 13 for iOS starting April 26th, 2025, and a minimum of iOS 13 for iOS starting May 27th, 2025. Currently, the MoEngage SDK supports [Android API level](https://source.android.com/docs/setup/reference/build-numbers) \[Placeholder\_Link] 21 and 22 (Android 5, Lollipop) for Android and iOS 12 for iOS devices. Releases after April 26th, 2025, for Android and May 27th, 2025, for iOS, will increase the minimum supported:
* API level from 21 to 23 (Android 6, Marshmallow) for Android
* iOS 12 to iOS 13 for iOS
# Why Are We Discontinuing Support for API Level 21 and 22 and iOS 12 in MoEngage SDK?
## For Android
The Lollipop platform and iOS 12 are almost 8 and 6 years old respectively. A very small percentage of all Android devices are using these or lesser versions. You can read more about the current distribution of Android devices. We believe that many of these old devices are not actively being used.
Many developers have already discontinued support for these versions in their apps. This helps them build better apps that make use of the newer capabilities of the platforms. For MoEngage, the situation is the same. By making this change, MoEngage can provide a more robust collection of tools for developers with greater speed.
# What Does This Mean for Your App That Uses MoEngage SDK?
You may use versions of MoEngage SDK released before April 26th, 2025 (for Android) and May 27th, 2025 (for iOS), as you are currently using. These versions will continue to work with Lollipop and iOS 12 devices, as they have worked in the past
When you choose to upgrade to the future versions released after April 26th, 2025 (for Android) and May 27th, 2025 (for iOS), you will not encounter any versioning problems if your app supports:
* Android API level 23 or greater (typically specified as “minSdkVersion” in your build.gradle)
* iOS 13
However, if your app supports lower than API level 21 or 22 or iOS 12, you will encounter a problem at build time with an error. This means, you will not be able to successfully run your app on older devices. To use MoEngage SDK versions released after April 26th, 2025 (for Android) and May 27th, 2025 (for iOS), you can choose one of the following options:
## For iOS
To discontinue support for iOS 12, perform the following changes in the **General** tab of your target XCode project:
1. Select the minimum iOS version you want to support in the **Deployment Target** drop-down list.
2. If your app is compatible with multiple devices, make sure to set the minimum iOS version for each device type (iPhone, iPad, and so on) in the same **Deployment Info** section.
## For Android
* Target API level 23 as the minimum supported API level (recommended)
To discontinue support for API levels that are no longer supported by the MoEngage SDK, increase the minSdkVersion value in your app’s build.gradle to at least 23. If you update your app in this way and publish it to the Play Store, users of devices with less than that level of support cannot see or download the update. However, they can still download and use the most recently published version of the app that does target their device.
For Android devices, if your app still has a significant number of users on older devices, you can use multiple APK support in Google Play to deliver an APK that uses lower versions of the MoEngage SDK. This is described below.
* Build multiple APKs to support devices with an API level less than 23 (not recommended).
With some configuration and code management, you can [build multiple APKs](https://developer.android.com/training/multiple-apks/api.html) \[Placeholder\_Link] that support different minimum API levels, with different versions of the MoEngage SDK. You can accomplish this with [build variants](https://developer.android.com/studio/build/build-variants.html) \[Placeholder\_Link] in Gradle. First, define build flavors for legacy and newer versions of your app. For example, in your build.gradle, define two different product flavors, with two different compile dependencies for the components of Play Services you are using:
```Kotlin Kotlin theme={null}
productFlavors {
legacy {
minSdkVersion 9??
versionCode 901 // Min API level 9, v01??
}
current {
minSdkVersion 14??
versionCode 1401 // Min API level 14, v01??
}
}
dependencies {
legacyCompile 'com.google.android.gms:play-services:10.0.0'??
currentCompile 'com.google.android.gms:play-services:10.2.0'??
}
```
In the situation above, there are two product flavors being built for two different versions of the Google Play services client libraries. This will work fine if only APIs available in lower MoEngage SDK versions are called. If you need to call newer APIs made available with versions released after April 26th, 2025, you must create a compatibility library for the newer API calls so that they are only built into the version of the application that can use them:
* Declare a Java interface that exposes the higher-level functionality you want to perform that is only available in the current versions of Play services.
* Build two Android libraries that implement that interface. The *current* implementation must call the newer APIs as desired. The *legacy* implementation must no-op or otherwise act as desired with older versions of the MoEngage SDK. You must add the interface to both libraries.
* Conditionally compile each library into the app using *legacyCompile* and *currentCompile* dependencies.
* In the app’s code, call through to the compatibility library whenever newer MoEngage SDK APIs are required.
After building a release APK for each flavour, publish them both to the Play Store, and the device will update with the most appropriate version for that device. Read more about [multiple APK support in the Play Store](https://developer.android.com/google/play/publishing/multiple-apks). \[Placeholder\_Link]
For any queries, please contact your MoEngage Customer Success Manager (CSM) or the Support team.
# SDK Deprecation Policy
Source: https://moengage.com/docs/developer-guide/sdk-lifecycle-and-policies/sdk-deprecation-policy
Learn how MoEngage deprecates SDK versions, when you are notified, what happens to your integration on a deprecated version, and how to check your version status.
As newer SDK versions ship, older versions are deprecated. This page explains how that process works and what it means for your integration.
MoEngage uses three version status levels:
* **Current** — the latest major version. It receives new features, fixes, and support.
* **Supported** — an older major version within its 3-year support window. It continues to receive support.
* **Deprecated** — a version outside its support window. It no longer receives fixes or support.
## Support Window
* MoEngage supports each major SDK version for 3 years from its initial release.
* Deprecations take effect on an annual cycle in August — a major version is deprecated in the first August on or after the end of its 3-year support window.
* MoEngage gives 6 to 12 months' advance notice before the deprecation date, and publishes the date for each version on the platform's deprecated-versions page.
* The 3-year window is measured from a major version's initial release, but a major version continues to receive minor and patch updates throughout its life. Because of this, the published deprecation boundary is a specific minor or patch version, not just the major version number — a platform can deprecate earlier builds of a major version while later builds of that same major remain supported. Check your platform's deprecated-versions page for the exact version boundary.
## Impact of SDK Deprecations on Your App
### Data Flow
MoEngage does not block traffic from deprecated versions. Apps already installed on your users' devices continue to send data, and MoEngage continues to receive it. Push, in-app, and analytics continue to function, subject to the capabilities of the SDK version you use.
### Bug Fixes and Security Patches
MoEngage ships bug fixes and security patches only in the current major version. Supported and deprecated versions do not receive patches. To receive a fix, upgrade to the current version.
### New Development
Existing production apps continue to run without change. For new app builds, use the current SDK version to get the latest fixes and capabilities. MoEngage removes deprecated versions from public package registries and app store tooling.
## Deprecation Notifications
When a version is scheduled for deprecation, you receive notice 6 to 12 months ahead of the date, so you can plan the upgrade as part of your normal release cycle.
MoEngage sends notifications through multiple channels:
* **Quarterly SDK newsletter.** MoEngage includes deprecation announcements in regular SDK updates.
* **Email to workspace administrators.** MoEngage sends the formal notice ahead of the deprecation date and repeats it as the date approaches.
* **In-dashboard notice.** MoEngage displays an alert in the dashboard.
* **Release notes.** MoEngage flags deprecation-related changes against the affected version in each release.
To receive these notices, keep your workspace administrator email current and subscribe to the SDK newsletter.
## Frequently Asked Questions
Check the deprecated versions list for your platform in the MoEngage developer documentation. Links for every platform are listed under [Next Steps](#next-steps).
This policy applies to client-side mobile and web SDKs only. Server-side and REST API integrations are governed separately. Contact your MoEngage Customer Success Manager (CSM) or the Support team for details.
## Next Steps
* Check your platform's deprecated versions list and release notes. MoEngage updates each list as versions move from current to supported to deprecated:
* [Android SDK Deprecated Versions](/docs/developer-guide/android-sdk/deprecated-versions/deprecated-versions) · [release notes](/docs/release-notes/sdks/android)
* [iOS SDK Deprecated Versions](/docs/developer-guide/ios-sdk/deprecated-versions/deprecated-versions) · [release notes](/docs/release-notes/sdks/ios)
* [Web SDK Deprecated Versions](/docs/developer-guide/web-sdk/deprecated-versions/deprecated-versions) · [release notes](/docs/release-notes/sdks/web)
* [Flutter SDK Deprecated Versions](/docs/developer-guide/flutter-sdk/deprecated-versions/deprecated-versions) · [release notes](/docs/release-notes/sdks/flutter)
* [React Native SDK Deprecated Versions](/docs/developer-guide/react-native-sdk/deprecated-versions/deprecated-versions) · [release notes](/docs/release-notes/sdks/react-native)
* [Unity SDK Deprecated Versions](/docs/developer-guide/unity-sdk/deprecated-versions/deprecated-versions) · [release notes](/docs/release-notes/sdks/unity)
* [Cordova SDK Deprecated Versions](/docs/developer-guide/cordova-sdk/deprecated-versions/deprecated-versions) · [release notes](/docs/release-notes/sdks/cordova)
* [Capacitor SDK Deprecated Versions](/docs/developer-guide/capacitor-sdk/deprecated-versions/deprecated-versions) · [release notes](/docs/release-notes/sdks/capacitor)
* Use the release checklist for your platform to plan and execute your upgrade:
* [Android SDK release checklist](/docs/developer-guide/android-sdk/checklist/release-checklist)
* [iOS SDK release checklist](/docs/developer-guide/ios-sdk/checklist/release-checklist)
* For help, contact your MoEngage Customer Success Manager (CSM) or the Support team.
# TV and OTT Integrations
Source: https://moengage.com/docs/developer-guide/tv-and-ott-integrations/getting-started/tv-and-ott-integrations
Integrate MoEngage with TV and OTT platforms to track user behavior and send personalized messages.
MoEngage empowers marketers and product managers to understand the behavior of their users and employ multiple communication channels to engage with those users on various TV Operating Systems.
You can capture customers' activities, show personalized, timely messages using communication channels like In-App, On-Site Messages, Cards, Web Personalization (in the case of Web-based platforms), and Push, and also capture customers' interactions with these messages.
Self-handled In-apps / Cards tend to be a predominant use-case in the case of TV Operating Systems so that you can custom handle the JSON payload (or content) delivered by MoEngage and show the content based on your TV app user experience.
# Supported Platforms and Features
| Platform or Channels and Features | Description | Framework |
| --------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Data Tracking | Data tracking is available for all TV OS types. The data will be tracked with the *Platform* being set to "TV" and the \_OS Type\_being set to the OS version of the TV device, such as, "Android TV", "Fire TV", etc. | [Android Native](/docs/developer-guide/android-sdk/sdk-integration/basic-integration/Install-Using-BOM), [iOS Native](/docs/developer-guide/ios-sdk/sdk-integration/basic/sdk-integration), [React Native](/docs/developer-guide/react-native-sdk/overview/getting-started-with-react-native-sdk) |
| Push | Android TV currently supports Push notifications, and this feature will be released soon for Amazon Fire TV. However, it is not supported by any other TV platform. **Note**: By default, Push notifications aren't supported on Android TV unless the app is system-whitelisted. | Releasing soon |
| In-App or OSM | Depending on the OS, overlay campaigns are supported on TV devices via In-apps or OSM for TV. [TV In-app campaigns](https://www.moengage.com/docs/user-guide/campaigns-and-channels/in-app-message/create/create-in-app-campaign-for-tv) are supported for Android TV, Apple TV, Roku TV and Amazon Fire TV. [TV OSM Campaigns](https://www.moengage.com/docs/user-guide/campaigns-and-channels/on-site-message/create/create-osm-campaign-for-tv) are supported by LG webOS, Samsung Tizen OS, VizioTV, and Xbox. | [Android Native](/docs/developer-guide/android-sdk/sdk-integration/basic-integration/Install-Using-BOM), [iOS Native](/docs/developer-guide/ios-sdk/sdk-integration/basic/sdk-integration), [React Native](/docs/developer-guide/react-native-sdk/overview/getting-started-with-react-native-sdk) |
| Cards | Cards are supported in LG webOS, Samsung Tizen OS, VizioTV, and Xbox. To learn more, see [Create a Card Campaign](https://www.moengage.com/docs/user-guide/campaigns-and-channels/cards/create/create-a-card-campaign). **Note**: Only [Self Handled Cards](/docs/developer-guide/react-native-sdk/cards/self-handled-cards#get-cards-info) are supported for TV. | |
| Web Personalization | This is being released soon for all TV OS types. | Releasing soon |
| | | |
For any help with TV and OTT integrations, please contact your MoEngage Customer Success Manager (CSM) or the Support team.
# Compliance
Source: https://moengage.com/docs/developer-guide/unity-sdk/compliance/compliance
Opt out of data tracking or enable and disable the MoEngage SDK in your Unity application.
# Opt-Out Of Data Tracking
To opt-out of data tracking by the SDK use the **optOutDataTracking( )** method as shown below. Once you have opted out of data tracking you need to explicitly opt-in to start tracking any event OR attributes for the user.
```c# c# theme={null}
// shouldOptOut: Bool indicating opt-out status, set true if you want to opt-out
MoEngageClient.optOutDataTracking(shouldOptOut);
```
# Enable/Disable SDK
If you don't want the MoEngage SDK to track any user information or send any data to the MoEngage System use the **DisableSdk()** method as shown below:
```c# c# theme={null}
MoEngageClient.DisableSdk();
```
Once this API is called all the SDK APIs will be non-operational. SDK will be disabled until **EnableSdk()** is called.\
Once you have the user's consent use the below API to enable the SDK.
```c# c# theme={null}
MoEngageClient.EnableSdk();
```
The above methods are available from the Unity Plugin version 1.2.0.
# Native SDK Initialisation
Based on the compliance policy you can optionally choose to initialize the SDK in a disabled state. To do so you can pass in a boolean value stating the SDK state as disabled while initializing the SDK.
## ANDROID:
```Kotlin Kotlin theme={null}
import com.moengage.unity.wrapper.MoEInitializer
import com.moengage.core.MoEngageimport com.moengage.core.DataCenterimport com.moengage.core.model.SdkState
// this is the instance of the application class and "YOUR_WORKSPACE_ID" is the Workspace ID from the dashboard.
val moEngage = MoEngage.Builder(this, "YOUR_WORKSPACE_ID",[YOUR_DATA_CENTER])
MoEInitializer.initialize(getApplicationContext(), moEngage, sdkState)
```
```Java Java theme={null}
import com.moengage.unity.wrapper.MoEInitializer;
import com.moengage.core.MoEngage;import com.moengage.core.DataCenter;import com.moengage.core.model.SdkState;
// this is the instance of the application class and "YOUR_WORKSPACE_ID" is the Workspace ID from the dashboard.
MoEngage.Builder moEngage =
new MoEngage.Builder(this, "YOUR_WORKSPACE_ID",[YOUR_DATA_CENTER]);
MoEInitializer.initialiseDefaultInstance(applicationContext, moEngage, sdkState)
```
## iOS:
In the case of iOS, SDK is initialized in the `MoEUnityAppController` class. Here update the initialization method to include the disabled SDK parameter as shown below:
```Objective-C Objective-C theme={null}
@implementation MoEUnityAppController
- (instancetype)init
{
self = [super init];
if (self) {
UnityRegisterAppDelegateListener(self);
}
return self;
}
# pragma mark - UIApplicationDelegate methods
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
[super application:application didFinishLaunchingWithOptions:launchOptions];
// SDK Initialization, with SDK State(isSdkEnabled) as MoEngageSDKStateDisabled for disabling the SDK
[[MoEUnityInitializer sharedInstance] initializeSDKWithLaunchOptions:launchOptions withSDKState:MoEngageSDKStateDisabled];
return YES;
}
@end
```
# Delete User From MoEngage Server
Source: https://moengage.com/docs/developer-guide/unity-sdk/data-tracking/delete-user-from-moengage-server
Delete the current user from the MoEngage server using the Unity SDK on Android.
This API is supported in **MoEngage Unity Package** starting **3.1.0** and is only available for the Android platform. Download the assets from [here](https://github.com/moengage/MoEngage-Unity-SDK/releases/tag/moengage-v3.1.0).
To delete the current user from the MoEngage server use the **DeleteUser(UserDeletionResponseDelegate)** method as shown below, **UserDeletionResponseDelegate**is a delegate function with **UserDeletionData**parameter, which is triggered when the user deletion is completed.
```javascript c# theme={null}
using MoEngage;
public void DeleteUser() {
Debug.Log("DeleteUser() : ");
MoEngageClient.DeleteUser(MyUserDeletionResponseDelegate);
}
//Delegate function
public void MyUserDeletionResponseDelegate(UserDeletionData data) {
Debug.Log("MyUserDeletionResponseDelegate() : isSuccess: " + data.isSuccess);
}
```
# Enable Advertising Identifier Tracking
Source: https://moengage.com/docs/developer-guide/unity-sdk/data-tracking/enable-advertising-identifier-tracking
Enable advertising identifier tracking in your Unity app using the MoEngage SDK for analytics.
For accurate analytics around devices and tracking Re-installs, it is essential to track the Advertising Identifier.
## Add Ad Identifier Library
Add the below dependency in the application level ***build.gradle*** file.
```groovy Groovy theme={null}
implementation("com.google.android.gms:play-services-ads-identifier:18.0.1")
```
To enable Advertising Identifier tracking use the *enableAdIdTracking()* method as shown below.
```javascript c# theme={null}
using MoEngage;
MoEngageClient.EnableAdIdTracking();
```
Before you enable Advertising Id tracking please ensure the application is complying with the [Google Play Policy](https://support.google.com/googleplay/android-developer/answer/10144311) regarding Advertising Id tracking. Refer to our [help document](https://www.moengage.com/docs/user-guide/data/privacy/android-advertising-id-tracking) for more information on the policy.
In case, you need to disable advertising-id after enabling tracking use the following method.
```javascript c# theme={null}
using MoEngage;
MoEngageClient.DisableAdIdTracking();
```
The above APIs are available only starting plugin version 2.3.0. In the older versions, Advertising Identifier tracking is enabled by default.
# Install/Update Differentiation
Source: https://moengage.com/docs/developer-guide/unity-sdk/data-tracking/install-update-differentiation
Differentiate between app installs and updates in your Unity app using the MoEngage setAppStatus API.
SDK needs support to enable the update by the user application or install the application. You need to have logic on the app side to distinguish between app *INSTALL* and *UPDATE*.
If the user was already using your application and has just updated to a new version that has MoEngage SDK, it is an update. Call the below API
```c# c# theme={null}
using MoEngage;
MoEngageClient.SetAppStatus(MoEAppStatus.UPDATE);
```
In case it is a fresh install call the below API
```c# c# theme={null}
using MoEngage;
MoEngageClient.SetAppStatus(MoEAppStatus.INSTALL);
```
# Tracking Events
Source: https://moengage.com/docs/developer-guide/unity-sdk/data-tracking/tracking-events
Track custom user events and their properties in your Unity app using the MoEngage TrackEvent API.
SDK adheres to the MoEngage FUP policies. For more information, refer to the [Fair Usage Policy](https://www.moengage.com/docs/user-guide/data/key-concepts/fair-usage-policy-fup).
Tracking events is how you record any actions your users perform, along with any properties that describe the action. Every trackEvent call records a single user action. We recommend that you make your event names human-readable so that everyone on your team can know what they mean instantly.
Every **TrackEvent()** call expects 2 parameters, event name and **Properties** instance which represent additional event attributes about the event. Add all the additional information which you think would be useful for segmentation while creating campaigns. For eg: the following example shows an example of tracking an event with all the possible data types.
```c# c# theme={null}
// Create Properties instance with all the event attributes info
Properties properties = new Properties()
.AddBoolean("booleanAttr", true)
.AddDouble("doubleAttr", 12.34)
.AddInteger("intAttr", 123)
.AddLocation("locationAttr", new GeoLocation(12.21, 13.42))
.AddISODateTime("dateAttr", "2019-01-02T08:26:21.170Z")
.AddString("stringAttr", "test String");
// Track Event to track the Event
MoEngageClient.TrackEvent("UnityEvent", properties);
```
Event names should not contain any special characters other than "\_". It can contain just spaces and an underscore.
# Analytics
MoEngage SDK has started tracking user sessions and application traffic sources.
To learn more about how user session and application traffic source tracking works, refer to the following docs:
* [Session and Source Analysis](https://www.moengage.com/docs/user-guide/analyze/analytics/session-and-source/session-and-source-analysis)
* [Advanced Session and Source Analysis](https://www.moengage.com/docs/user-guide/analyze/analytics/session-and-source/advanced-session-and-source-analysis)
With user session tracking we have introduced the flexibility to selectively mark events as non-interactive.
## What is a non-interactive event?
Events that do not affect the session calculation in anyways are called non-interactive events. Non-interactive events have the below properties
* Do not start a new session.
* Do not extend the session.
* Do not have information related to a user session.
## How to mark an event as non-interactive?
To mark an event as a non-interactive call **SetNonInteractiveEvent()** for **Properties** instance as shown below:
```c# c# theme={null}
// Create Properties instance with SetNonInteractive()
Properties properties = new Properties()
.AddBoolean("booleanAttr", true)
.AddString("stringAttr", "test String")
.SetNonInteractive();
// Track Event to track the Event
MoEngageClient.TrackEvent("NonInteractive Event", properties);
```
# Tracking User Attributes
Source: https://moengage.com/docs/developer-guide/unity-sdk/data-tracking/tracking-user-attributes
Track user attributes and manage login and logout states using the MoEngage Unity SDK.
User Attributes are pieces of information you know about a user which could be demographics like age or gender, account-specific like plan, or even things like whether a user has seen a particular A/B test variation. It's up to you! It is basically a customer identity that you can reference across the customer’s whole lifetime.
# Implementing Login/Logout
* It's important to set the User Attribute Unique ID when a user logs into your app.
* This is to merge the new user with the existing user, if any exists, and will help prevent creation of unnecessary/stale users.
* Setting the Unique ID is a critical piece to tie a user across devices and installs/uninstalls as well across all platforms (i.e. iOS, Android, Windows, The Web). Set the **USER\_ATTRIBUTE\_UNIQUE\_ID** attribute as soon as the user is **logged in**. Unique ID can be something like an email ID, a username (unique), or a database ID or any Backend generated ID.
* Do not set this for the user who not logged in.
## Login
```c# c# theme={null}
using MoEngage;
MoEngageClient.IdentifyUser("UNIQUE ID");
```
**Note:** The following values are not allowed in the UniqueID field: "unknown", "guest", "null", "0", "1", "true", "false", "user\_attribute\_unique\_id", "(empty)", "na", "n/a", "", "dummy\_seller\_code", "user\_id", "id", "customer\_id", "uid", "userid", "none", "-2", "-1", "2"
### Set a Single Identity
Use `MoEngageClient.IdentifyUser()` to set a single user identifier:
```c# c# theme={null}
using MoEngage;
MoEngageClient.IdentifyUser("UNIQUE_ID"); // Pass any unique value for your user
```
### Set Multiple User Identities
Use `MoEngageClient.IdentifyUser()` with a dictionary to set multiple user identities at once:
```c# c# theme={null}
using MoEngage;
var identities = new Dictionary {
{ "uniqueId", "UNIQUE_ID" },
{ "email", "user@example.com" },
{ "mobile", "+911234567890" }
};
MoEngageClient.IdentifyUser(identities);
```
If a network issue prevents the identifier from reaching the MoEngage server, the SDK caches the `identifyUser()` call and retries automatically once the network is available or the next time the app opens.
For more information, refer to:
* [Enable/Disable SDK](/docs/developer-guide/unity-sdk/compliance/compliance#enabledisable-sdk)
* [Opt-Out Of Data Tracking](/docs/developer-guide/unity-sdk/compliance/compliance#opt-out-of-data-tracking)
## Logout
The application needs to notify the MoEngage SDK whenever the user is logged out of the application. To notify the SDK, call the API whenever the user is logged out of the application.
```c# c# theme={null}
using MoEngage;
MoEngageClient.Logout();
```
In case the application is registering for push token it should pass the new push token to MoEngage SDK after user logout. For more information about passing push tokens, refer to [Push Configuration for Android SDK](/docs/developer-guide/android-sdk/push/basic/push-configuration).
### Logout Complete Callback
To receive a callback when logout completes, register a listener using `MoECoreHelper.addLogoutCompleteListener()`:
```c# c# theme={null}
using MoEngage;
MoEGameObject.LogoutCompleteCallback += LogoutCompleteCallback;
public void LogoutCompleteCallback(object sender, LogoutCompleteData data) {
Debug.Log("LogoutCompleteCallback() : platform=" + data.platform + " appId=" + data.accountMeta.appId);
}
```
**Updates to SDK functions for User Identification and Session Management**
* **Forced Logout:** The MoEngage SDK no longer automatically logs out the previous user when a new user is detected on the device. Logout should now be explicitly called for workspaces enabled with Identity resolution to avoid data corruption.
* **SetUniqueID:** `identifyUser` function supports multiple identifiers, which replaces the need of using `SetUniqueID` function for user identification. Note that `SetUniqueID` is marked for removal in the future releases of SDK versions - it is important to use `identifyUser` instead especially if you are using Identity resolution in your workspace.
* **SetAlias:** For workspaces with the Identity resolution feature enabled, MoEngage SDK stores the previous identifier values. When `identifyUser` function is used to track the new identifier values, MoEngage SDK detects the change in identifier value and reports accordingly.
If you call the `identifyUser` function without logging out, then the existing logged-in user's ID is updated.
If you call `identifyUser()` multiple times with different identifier names, the SDK will append this identifier to the already set identifiers.
## Updating User Attribute Unique Id
Use the method *setAlias()* to update the user attribute unique id instead of *IdentifyUser()* with a different value. Using the method *IdentifyUser()* with a new value creates unintended users in MoEngage.
```c# c# theme={null}
using MoEngage;
MoEngageClient.SetAlias("Updated Unique Id");
```
Please make sure that you use **SetAlias()** for updating the Unique Identifier and not **IdentifyUser()** as calling **IdentifyUser()** with a new value will reset the current user and lead to the creation of unintended users in our system.
# Tracking User Attributes
The SDK provides APIs to track commonly tracked user attributes like First Name, Last Name, Email-Id, etc. Please use the provided methods for tracking these attributes.
```c# c# theme={null}
using MoEngage;
MoEngageClient.SetFirstName("First Name");
MoEngageClient.SetLastName("Last Name");
MoEngageClient.SetEmail("Email");
MoEngageClient.SetPhoneNumber("Phone number");
MoEngageClient.SetGender(MoEUserGender.MALE); // MoEUserGender.FEMALE
MoEngageClient.SetUserLocation(new GeoLocation(23, 44));
MoEngageClient.SetBirthdate("2020-01-02T08:26:21.170Z"); // ISO Format : yyyy-MM-dd'T'HH:mm:ss.fff'Z'
```
For setting other User Attributes you can use the generic method **SetUserAttribute(key, value)**
```c# c# theme={null}
using MoEngage;
MoEngageClient.SetUserAttribute(key, value);
```
## Tracking Date as user attributes
**ISO date**
Use `SetUserAttributeISODate()` to track a date attribute using an ISO 8601 string.\
Date format: `yyyy-MM-dd'T'HH:mm:ss.fff'Z'`
```c# c# theme={null}
using MoEngage;
MoEngageClient.SetUserAttributeISODate("userAttrDate", "2019-01-02T08:26:21.170Z");
```
**Epoch time**
Use `SetUserAttributeEpochTime()` to track a date attribute using a Unix epoch timestamp (milliseconds).
```c# c# theme={null}
using MoEngage;
MoEngageClient.SetUserAttributeEpochTime("userAttrDate", 1546417581170L);
```
## Tracking Location as user attributes
To track any location as user attributes use the ***SetUserAttributeLocation()***. This API takes the attribute name and an instance of ***GeoLocation*** for coordinates as input.\
Example:
```c# c# theme={null}
using MoEngage;
MoEngageClient.SetUserAttributeLocation("bangalore", new GeoLocation(23, 44));
```
## Custom Boolean User Attribute
### iOS(optional)
Starting from version 4.x.x of MoEngage.unityPackage, the default tracking for the custom boolean attribute will be changed to ***bool(true/false***) from ***double(0/1)***. To configure this, enable ***should Track Boolean As Number*** variable of MoGameObject script as shown below to track the boolean as double . By default it is ***disabled*** to track boolean as true/false.
Refer to the example code below for tracking the boolean user attribute.
```c# c# theme={null}
using MoEngage;
// If `should Track Boolean As Number` is passed as true then `boolean attribute True` will tracked with value 1 else true
MoEngageClient.SetUserAttribute("boolean attribute true",true);
// If `should Track Boolean As Number` is passed as true then `boolean attribute false` will tracked with value 0 else false
MoEngageClient.SetUserAttribute("boolean attribute false",false);
```
## Reserved keywords for User Attributes
Below is the list of keys that should not be used when tracking user attributes.
* USER\_ATTRIBUTE\_UNIQUE\_ID
* USER\_ATTRIBUTE\_USER\_EMAIL
* USER\_ATTRIBUTE\_USER\_MOBILE
* USER\_ATTRIBUTE\_USER\_NAME
* USER\_ATTRIBUTE\_USER\_GENDER
* USER\_ATTRIBUTE\_USER\_FIRST\_NAME
* USER\_ATTRIBUTE\_USER\_LAST\_NAME
* USER\_ATTRIBUTE\_USER\_BDAY
* USER\_ATTRIBUTE\_NOTIFICATION\_PREF
* USER\_ATTRIBUTE\_OLD\_ID
* MOE\_TIME\_FORMAT
* MOE\_TIME\_TIMEZONE
* USER\_ATTRIBUTE\_DND\_START\_TIME
* USER\_ATTRIBUTE\_DND\_END\_TIME
* MOE\_GAID
* INSTALL
* UPDATE
* MOE\_ISLAT
* status
* user\_id
* source
# Unity SDK Deprecated Versions
Source: https://moengage.com/docs/developer-guide/unity-sdk/deprecated-versions/deprecated-versions
Check the support status of MoEngage Unity SDK major versions: which are current, which are still supported, and which are deprecated, along with deprecation dates.
Use this page to check the support status of MoEngage Unity SDK major versions. It lists which versions are current, which are still supported, and which are deprecated, along with deprecation dates. This is the source of truth for Unity SDK version status. For how the lifecycle works and what deprecation means for your app, see the [SDK Deprecation Policy](/docs/developer-guide/sdk-lifecycle-and-policies/sdk-deprecation-policy). To set up the Unity SDK, see the [integration guide](/docs/developer-guide/unity-sdk/sdk-integration/sdk-installation/sdk-installation).
## Version Support Status
**Current** — The latest major version. It receives new features, fixes, and support.
**Supported** — An older major version within its 3-year support window. It continues to receive support.
**Deprecated** — A version outside its support window. It no longer receives fixes or support.
| Major Version | Status | Deprecation Date | Notes |
| -------------------- | ---------- | ---------------- | -------------------------------------------------------------------------------------------------------------- |
| Core 6.x | Current | TBD | Latest major version. Receives new features, fixes, and support. |
| Core 5.x | Supported | TBD | Receives support. |
| Core 4.x | Supported | TBD | Receives support. |
| Core 3.2.0 and below | Deprecated | August 2027 | Support, debugging, and fixes are no longer available. Data continues to flow. Upgrade to the current version. |
MoEngage supports each major version for 3 years from its initial release; deprecation then takes effect the following August. See the [SDK Deprecation Policy — Support Window](/docs/developer-guide/sdk-lifecycle-and-policies/sdk-deprecation-policy#support-window) for details. This table is updated when MoEngage announces further deprecations. To receive advance notice, subscribe to the SDK newsletter and ensure your workspace administrator email is current.
## Understanding Deprecated Status
A deprecated version continues to send and receive data. MoEngage does not block traffic. After the deprecation date, MoEngage no longer debugs, fixes, or supports issues specific to that version, and the resolution for any issue is to upgrade to the current version. All fixes ship in the current version only and are not available on supported or deprecated versions. For the complete policy, see the [SDK Deprecation Policy](/docs/developer-guide/sdk-lifecycle-and-policies/sdk-deprecation-policy).
## Upgrading to a Supported Version
* Refer to the [Unity SDK release notes](/docs/release-notes/sdks/unity) for the current major version changes.
* Review the [MoEngage-Unity-SDK](https://github.com/moengage/MoEngage-Unity-SDK) repository for the latest packages.
* For help, contact your MoEngage Customer Success Manager (CSM) or the Support team.
## Frequently Asked Questions
Apps on deprecated versions continue to run and data continues to flow. However, MoEngage no longer provides support, debugging, or fixes for deprecated versions. Upgrading to the current or a supported version using the [Unity SDK release notes](/docs/release-notes/sdks/unity) ensures you receive continued support.
MoEngage sends advance notice 6 to 12 months before the deprecation date through email to workspace administrators, the quarterly SDK newsletter, in-dashboard notices, and SDK release notes. See the [SDK Deprecation Policy](/docs/developer-guide/sdk-lifecycle-and-policies/sdk-deprecation-policy) for the full notification schedule.
# InApp NATIV
Source: https://moengage.com/docs/developer-guide/unity-sdk/in-app-messages/inapp-nativ
Display contextual in-app NATIV campaigns in your Unity app using the MoEngage SDK.
In-App NATIV Campaigns target your users by showing a message while the user is using your app. They are very effective in providing contextual information and help to cross-sell/up-sell on desired screens of your app or/and on desired actions performed by the user in your app.
## Installing Android Dependency
Add the following dependency to the **mainTemplate.gradle** file.
```gradle Groovy theme={null}
dependencies {
...
implementation("com.moengage:inapp:$sdkVersion")}
```
replace **\$sdkVersion** with the appropriate SDK version
### **Requirements for displaying images and GIFs in InApp**
Starting InApp version **7.0.0,** SDK requires [Glide](https://bumptech.github.io/glide/) to show images and GIFs in the in-apps. You need to add the below dependency in your **mainTemplate.gradle** file.
```gradle Groovy theme={null}
dependencies {
...
implementation("com.github.bumptech.glide:glide:4.9.0")
annotationProcessor("com.github.bumptech.glide:compiler:4.9.0")
}
```
# Display In-App
MoEngage refreshes the list of eligible In-App campaigns via a meta API call. During an active session, the list refreshes when the app returns from the background to the foreground (at most once every 15 minutes), and immediately when a new session starts or the app is killed and relaunched.
Call the below API to show an in-app message on a screen.
```c# c# theme={null}
using MoEngage;
MoEngageClient.ShowInApp();
```
# Show Nudge
Use `MoEInAppHelper.ShowNudge()` to display a nudge-type in-app message at a specified position on screen.
```c# c# theme={null}
using MoEngage;
MoEInAppHelper.ShowNudge(NudgePosition.Top);
```
The `NudgePosition` enum defines where the nudge appears:
| Value | Description |
| ------------- | ----------------------------------------------- |
| `Top` | Displays the nudge at the top of the screen. |
| `Bottom` | Displays the nudge at the bottom of the screen. |
| `BottomLeft` | Displays the nudge at the bottom-left corner. |
| `BottomRight` | Displays the nudge at the bottom-right corner. |
# Self-Handled InApps
Self-handled In-Apps are messages that the SDK delivers to the application, and the application builds the UI using the SDK's delivered payload.
## Single Self-Handled InApps
Call the below API to request a single self-handled in-app message.
```c# c# theme={null}
using MoEngage;
MoEngageClient.GetSelfHandledInApp();
```
The payload is returned via a callback. Register a callback as shown below.
```c# c# theme={null}
using MoEngage;
// register a callback
MoEGameObject.InAppSelfHandled += InAppSelfHandledCallback;
public void InAppSelfHandledCallback(object sender, InAppSelfHandledCampaignData inAppData) {
// process the self-handled payload here
string selfHandledPayload = inAppData.selfHandled.payload;
}
```
## Multiple Self-Handled InApps
Event-triggered multiple self-handled in-apps are not supported.
Use `MoEngageClient.GetSelfHandledInApps()` to fetch multiple self-handled campaigns. The SDK returns up to 5 campaigns in the order of campaign priority set at the time of campaign creation.
```c# c# theme={null}
using MoEngage;
MoEngageClient.GetSelfHandledInApps();
```
The list of campaigns is returned via a callback. Register a callback as shown below.
```c# c# theme={null}
using MoEngage;
// register a callback
MoEGameObject. += ;
public void (object sender, campaignsData) {
// iterate over the list of self-handled campaigns
foreach (var campaign in campaignsData.campaigns) {
string payload = campaign.selfHandled.payload;
}
}
```
### Tracking Statistics
The statistics for each campaign in the list must be tracked individually. Pass the individual `InAppSelfHandledCampaignData` object as a parameter to the APIs below.
```c# c# theme={null}
// call whenever in-app is shown
MoEngageClient.SelfHandledShown(campaign);
// call whenever in-app is clicked
MoEngageClient.SelfHandledClicked(campaign);
// call whenever in-app is dismissed
MoEngageClient.SelfHandledDismissed(campaign);
```
# InApp Callbacks
SDK provides callbacks to the client application whenever is shown, dismissed or clicked(only if there is a navigation action or custom action associated with the widget).\
Use the below callbacks to get notified for the above cases
## InApp Shown
```c# c# theme={null}
using MoEngage;
// register a callback
MoEGameObject.InAppShown += InAppShownCallback;
public void InAppShownCallback(object sender, InAppData inappData){
Debug.Log(TAG + " InAppShownCallback() : ");
}
```
## InApp Clicked
```c# c# theme={null}
using MoEngage;
// register a callback
MoEGameObject.InAppClicked += InAppClickedCallback;
public void InAppClickedCallback(object sender, InAppClickData inAppData)
{ Debug.Log(TAG + " InAppClickedCallback() : ");
}
```
## InApp Custom Action
```c# c# theme={null}
using MoEngage;
// register a callback
MoEGameObject.InAppCustomAction += InAppCustomActionCallback;
public void InAppCustomActionCallback(object sender, InAppClickData inAppData)
{
// key value pairs for custom aciton.
IDictionary keyValuePairs = ((CustomAction) inAppData.action).keyValuePairs; Debug.Log(TAG + " InAppCustomActionCallback() : keyValuePairs: " + keyValuePairs.ToString());
}
```
## InApp Dismissed
```c# c# theme={null}
using MoEngage;
// register a callback
MoEGameObject.InAppDismissed += InAppDismissedCallback;
public void InAppDismissedCallback(object sender, InAppData inAppData)
{ Debug.Log(TAG + " InAppDismissedCallback() : ");
}
```
## InApp Payload
```c# c# theme={null}
/// InApp Campaign model
public class InAppData {
/// Account info
public AccountMeta accountMeta;
/// InApp campaign info
public InAppCampaign campaignData;
/// Native platform from which the callback was triggered.
public Platform platform;
}
/// Meta-data related to your MoEngage account.
public class AccountMeta {
/// Account Identifier
public string appId;
}
/// InApp Campaign Details
public class InAppCampaign {
/// Unique identifier for each campaign.
public string campaignId;
/// Campaign Name
public string campaignName;
/// Additional meta data of campaign
public InAppCampaignContext campaignContext;
}
/// Additonal meta of InAppCampaign
public class InAppCampaignContext {
/// Formatted campaign id
public string formattedCampaignId;
}
/// InApp model when click action is performed.
public class InAppClickData {
/// Account info
public AccountMeta accountMeta;
/// InApp campaign info
public InAppCampaign campaignData;
/// Native platform from which the callback was triggered.
public Platform platform;
/// Action info
public InAppClickAction action;
}
/// InApp Navigation action model.Available only in InAppClickedCallback()
public class NavigationAction: InAppClickAction {
/// Navigation action type
public ActionType actionType;
/// Type of Navigation action.Possible value deep_linking or screen
public NavigationType navigationType;
/// Deeplink Url or the Screen Name used for the action.
public string url;
/// Additional Key-Value pairs entered on the MoEngage Platform for navigation action of the campaign
public IDictionary < string, object > keyValuePairs;
}
/// Custom action performed on inapp.Available only in InAppCustomActionCallback()
public class CustomAction: InAppClickAction {
/// Custom Action type
public ActionType actionType;
/// Key-Value Pair entered on the MoEngage Platform during campaign creation.
public IDictionary < string, object > keyValuePairs;
} /// InApp SelfHandled model. Available on InAppSelfHandledCallback() callback
public class InAppSelfHandledCampaignData {
/// Account info
public AccountMeta accountMeta;
/// InApp campaign info
public InAppCampaign campaignData;
/// Native platform from which the callback was triggered.
public Platform platform;
/// SelfHandled payload info
public SelfHandled selfHandled;
}
/// SelfHandled Payload information
public class SelfHandled {
/// Self handled campaign payload.
public string payload;
/// Interval after which in-app should be dismissed, unit - Seconds
public long dismissInterval;
/// Should the campaign be dismissed by pressing the back button or using the back gesture. if the value is true campaign should be dismissed on back press.
public bool isCancellable;
}
```
# Handling Orientation Change
This is only for the Android platform
Starting Unity Plugin version 2.2.0 in-apps are supported in both portrait and landscape modes.\
SDK has to be notified when the device orientation changes for SDK to handle in-app displays.
There are two ways to do it:
1. Add the API call in the Android native part of your app
2. Call MoEngage plugin's **onOrientationChanged()**
3. Add the API call in the Android native part of your app
Notify the SDK when **onConfigurationChanged()** API callback is received in your UnityPlayerActivity class.
```java Java theme={null}
public class SampleUnityPlayerActivity extends UnityPlayerActivity {
...
@Override
public void onConfigurationChanged(@NonNull Configuration newConfig) {
super.onConfigurationChanged(newConfig);
//API to notify MoEngage SDK
MoEUnityHelper.getInstance().onConfigurationChanged();
...
}
...
}
```
If you don't want to create a custom **UnityPlayerActivity**, MoEngage SDK on Android comes bundled with **MoEUnityPlayerActivity** which internally handles the device configuration changes.\
This activity needs to be added as an entry point to your app, to do so replace your current entry point with the below code in your **AndroidManifest.xml**.
```xml XML theme={null}
...
...
```
## Call the MoEngage plugin's orientation change API
Call the below API to notify SDK of the orientation change.
```objectivec c# theme={null}
MoEngageClient.OnOrientationChanged();
```
# Android Notification Runtime Permissions
Source: https://moengage.com/docs/developer-guide/unity-sdk/push/basic/android-notification-runtime-permissions
Handle Android 13 notification runtime permissions in your Unity app using the MoEngage SDK.
This is supported from Plugin version **3.0.0**.
Android 13 (API level 33) and higher supports [runtime permission](https://developer.android.com/guide/topics/permissions/overview#runtime) for sending [non-exempt](https://developer.android.com/develop/ui/views/notifications/notification-permission#exemptions)(including Foreground Services (FGS)) notifications from an app [POST\_NOTIFICATIONS](https://developer.android.com/reference/android/Manifest.permission#POST_NOTIFICATIONS). This change helps users focus on the notifications that are most important to them.
Refer to the [official documentation](https://developer.android.com/develop/ui/views/notifications/notification-permission) for more details.
When the application is running on Android 13 to show notifications to the user, applications would need to request the user for notification permission.
For applications integrating the MoEngage SDK, would need to
* Notify the SDK of the permission request's response from the user.
* If the application has already requested push permission(before MoEngage integration) help MoEngage set up notification channels for notification display.
## Notify SDK of permission result
Once the application requests the user for notification permission notify the SDK of the user response using the below API. *If you are letting MoEngage SDK handle the notification permissions, you should ignore this step.*
```c# c# theme={null}
using MoEngage;
MoEngageClient.PushPermissionResponseAndroid(isGranted)
```
## Setup Notification Channels
If the application has already taken notification permission from the user call the below API to set up Notification Channels for showing push notifications.
```c# c# theme={null}
using MoEngage;
MoEngageClient.SetupNotificationChannelsAndroid()
```
## Let MoEngage SDK handle Notification permission
MoEngage SDK provides helper APIs to show the permission request to the end-user or navigate the user to the settings screen for enabling notifications.
Use the below API to show the permission request dialog to the user.
When using the below API SDK automatically tracks the response, sets up the required notification channel, etc, mentioned above.
```c# c# theme={null}
using MoEngage;
MoEngageClient.RequestPushPermissionAndroid()
```
## Navigate to Notification Settings (situational/optional)
Use the below API to show navigate the user to the Notification Settings for the application on the device.
*Note*: Below Android 8(API level 26) the user is directed to the Application Info screen of your application.
```c# c# theme={null}
using MoEngage;
MoEngageClient.NavigateToSettingsAndroid()
```
## Update the Permission request count
Once the application requests the user for notification permission, update the SDK of the request attempts.
### Why does the SDK require permission attempt count?
SDK requires the attempt count to accurately track the number of times the permission request was attempted. If the user denies the permission request twice the application/SDK cannot request permission further i.e. if we request permission it would be automatically denied without the user seeing the request dialog. As an SDK we cannot be sure of the number of times the user has denied the permission we try to optimize the calls to request permission based on the attempt count.
```c# c# theme={null}
using MoEngage;
MoEngageClient.UpdatePushPermissionRequestCountAndroid(count)
```
# Android Push Configuration
Source: https://moengage.com/docs/developer-guide/unity-sdk/push/basic/android-push-configuration
Configure Android push notifications in your Unity app including Firebase and push token setup.
## Basic Configuration
To use Push Notification in your Unity application, you need to configure Firebase into your application; refer to the following documentation to configure Push Notification in your application.
* [Push Notification](/docs/developer-guide/android-sdk/push/basic/push-configuration)
* [Configuring FCM](/docs/developer-guide/android-sdk/push/basic/push-token-registration-and-display)
In case your application is handling the push token registration and push payload, we highly recommend you use the native Android methods(mentioned in the documentation above) for passing the token and the payload to the SDK. If, for whatever reason, you wish to handle the token registration and handle incoming messages by yourself, pass the push token and payload to the SDK by using the below APIs
Also, make sure you have set up [Firebase for Unity](https://firebase.google.com/docs/cloud-messaging/unity/client).
NotificationConfig requires assets for small icon and large icon, depending your Unity version you will have to include these in either in **Assets/res** folder or include the raw assets in an Android Library Project or AAR. For more information on AAR, refer [here](https://docs.unity3d.com/2023.1/Documentation/Manual/android-library-project-and-aar-plugins-introducing.html).
## Passing Push Token
```c# c# theme={null}
using MoEngage;
MoEngageClient.PassFcmPushToken()
```
## Passing Push Payload
```c# c# theme={null}
using MoEngage;
MoEngageClient.PassFcmPushPayload()
```
We highly recommend you use the Android native APIs for passing the push payload to the MoEngage SDK instead of the Unity/C# APIs. Unity Engine might not get initialized if the application is in the killed state which will lead to poor push reachability or delivery.
## Customizing Push notification
If required the application can customize the behavior of notifications by using Native Android code (Java/Kotlin). To learn more about the customization refer to the [Advanced Push Configuration](/docs/developer-guide/android-sdk/push/advanced/callbacks-and-customisation) documentation.Instead of extending ***PushMessageListener*** as mentioned in the above document extend ***PluginPushCallback.***
Refer to the below documentation for Push Amp+, Push Templates, and Geofence.
* [Push Templates](/docs/developer-guide/android-sdk/push/optional/push-templates)
* [GeoFence Push](/docs/developer-guide/android-sdk/push/optional/location-triggered)
# iOS Push Configuration
Source: https://moengage.com/docs/developer-guide/unity-sdk/push/basic/ios-push-configuration
Configure iOS push notifications in your Unity app including APNS certificates and push registration.
# Configuring Push in iOS
## APNS Certificate
First, you will have to create an APNS certificate and upload to the dashboard to be able to send push notifications in iOS. Follow the steps below to do that :
* [Create an APNS certificate](/docs/developer-guide/ios-sdk/push/basic/apns-certificate-pem-file-legacy)
* [Convert the resultant certificate to .pem format](/docs/developer-guide/ios-sdk/push/basic/apns-certificate-pem-file-legacy)
* [Upload .pem file to MoEngage Dashboard](/docs/developer-guide/ios-sdk/push/basic/apns-certificate-pem-file-legacy)
\*Follow the links on each step to complete it.
## Push Registration
After this you will have to register for push notification in the App by using **RegisterForPush** method of the plugin as shown below:
```c# c# theme={null}
using MoEngage;
MoEngageClient.RegisterForPush();
```
## App Target Settings
MoEngage plugin takes care of setting up the project while building it for the first time. But verify the Capability section has `Push Notifications` enabled along with `AppGroups` and `Background Mode` Settings as shown below:
MoEngage plugin creates an app group for the app with format: `group..moengage`. Make sure the same is resolved with the Apple developer account here.
## Extensions
MoEngage plugin takes care of setting up the Extension target too. Make sure the same App Group Id is set for MoENotificationServiceExtension and MoEPushTemplateExtension.
## Provisional Push Authorization
Provisional authorization (available on iOS 12 and above) lets you send push notifications to users without requesting upfront permission. Notifications are delivered quietly to the Notification Center, where users can choose to keep or turn off notifications.
To enable provisional authorization, call the following API before registering for push:
```c# c# theme={null}
using MoEngage;
MoEngageClient.RegisterForProvisionalPush();
```
Provisional push is only available on iOS 12 and above. On earlier iOS versions, this call has no effect.
# Push Callback
Source: https://moengage.com/docs/developer-guide/unity-sdk/push/basic/push-callback
Set up push notification click observers to handle notification interactions in the MoEngage Unity SDK.
# Notification Click Observers
MoEngage plugin triggers the **PushNotifCallback** event whenever a notification is clicked. This event is a common trigger for both iOS and Android platforms. Refer to the below code to set the observer to the same:
```auto c# theme={null}
using MoEngage;
// Add Push Callback for MoEGameObject
MoEGameObject.PushNotifCallback += PushCallback;
//Implement the calback
public void OnPushClicked(object sender, PushCampaignData campaignData)
{
Debug.Log(" OnPushClicked() : Event handler callback: " + campaignData.platform +
" \n clickedAction: " + campaignData.data.clickedAction + " \n payload:" + campaignData.data.payload);
}
```
## PushCampaignData structure
PushCampaignData instance will have the below properties:
```auto c# theme={null}
public class PushCampaignData {
/// Account info
public AccountMeta accountMeta;
/// PushCampaign data object
public PushCampaign data;
/// Native platform from which the callback was triggered.
public Platform platform;
}
/// Meta-data related to your MoEngage account.
public class AccountMeta {
/// Account Identifier
public string appId;
}
/// Push Payload information
public class PushCampaign {
/// This key is present only for the Android Platform. It's a boolean value indicating if the user clicked on the default content or not. true if the user clicks on the default content else false.
public bool isDefaultAction;
/// Action to be performed on notification click.
public IDictionary < string, object > clickedAction;
/// Complete campaign payload.
public IDictionary < string, object > payload;
}
/// Platform on which Push Campaign belongs
public enum Platform {
iOS,
Android
}
```
Payload Structure for **clickedAction** Dictionary
```json JSON theme={null}
{
"clickedAction": {
"type": "navigation/customAction",
"payload": {
"type": "screenName/deepLink/richLanding",
"value": "",
"kvPair": {
"key1": "value1",
"key2": "value2",
...
}
}
}
}
```
**clickedAction**- Action to be performed on notification click.
**clickedAction.type**- Type of click action. Possible values **navigation** and **customAction**. Currently, **customAction** is supported only on Android.
**clickAction.payload** - Action payload for the clicked action.\
**clickedAction.payload.type** - Type of navigation action defined. Possible values **screenName**, **deepLink**, and **richLanding.**
Currently, in the case of iOS, rich landing and deep-link URLs are processed internally by the SDK and not passed in this callback; therefore possible value in the case of iOS is only **screenName**.\
**clickAction.value** - value entered for navigation action or custom payload.\
**clickAction.kvPair** - Custom key-value pair entered on the MoEngage Platform.\
**payload** - Complete campaign payload.
## Android Payload
If the user clicks on the default content of the notification, the key-value pair and campaign payload can be found inside the **payload** key. If the user clicks on the action button or a push template action, the action payload would be found inside **clickedAction**.\
You can use the **isDefaultAction** key to check whether the user clicked on the default content and then parse the payload accordingly.
## iOS Payload
In the case of iOS, you would always receive the key-value pairs with respect to clicked action in **clickedAction** property. Refer to this [link](/docs/developer-guide/ios-sdk/push/basic/ios-push-integration-tutorial) for knowing the iOS notification payload structure.
# Configuring Push Templates
Source: https://moengage.com/docs/developer-guide/unity-sdk/push/optional/configuring-push-templates
Import the RichNotifications package to add push notification templates in your Unity app.
To integrate Push Template, Go to Unity Editor, navigate to **Assets > Import Package > Custom Package,** and import the **RichNotifications.unitypackage**\
The above package is part of the Android folder in the **.zip** file you downloaded during installation.
# Location Triggered
Source: https://moengage.com/docs/developer-guide/unity-sdk/push/optional/location-triggered
Import the Geofence package to enable location-triggered push notifications in your Unity app.
To integrate Geofence, Go to Unity Editor, navigating to `Assets > Import Package > Custom Package` and import the `Geofence.unitypackage`. The package is part of the `.zip` file you downloaded during installation.
# Initialization
Add ***MoEGeofenceGameObject.cs*** to the same object created in the [Unity Initialization](/docs/developer-guide/unity-sdk/sdk-integration/sdk-initialization/sdk-initialization) step and set the ***App Id***
# Android Configuration
## Prerequisites for Geofence
To use geofencing, your app must request the following:
* ACCESS\_FINE\_LOCATION
* ACCESS\_BACKGROUND\_LOCATION if your app targets Android 10 (API level 29) or higher
For Geofence pushes to work your Application should have location permission and [Play Services' Location Library](https://developers.google.com/android/guides/setup#declare-dependencies) should be included.
Refer to the [documentation](https://developer.android.com/training/location/geofencing#RequestGeofences) for more details on Geofence.
# iOS Configuration
Post integration with the geofence package make sure to [Build and Replace](/docs/developer-guide/unity-sdk/sdk-integration/sdk-initialization/file-based-initialization/ios) for iOS.
# Configuration
## Start Geofence Monitoring
After integrating the geofence package call ***StartGeofenceMonitoring()*** method to initiate the geofence module, this will fetch the geofences around the current location of the user. Please take a look at the [iOS doc](/docs/developer-guide/ios-sdk/push/optional/location-triggered) and [Android doc](/docs/developer-guide/android-sdk/push/optional/location-triggered) for more information on Geofence. By default, the geofence feature is not enabled. You need to call the ***S***\*\**tartGeofenceMonitoring*()\*\*to receive location-triggered
```c# c# theme={null}
using MoEngage;
MoEngageGeofenceClient.StartGeofenceMonitoring();
```
## Stop Geofence Monitoring
If you want to stop the geofence monitoring or feature use the ***StopGeofenceMonitoring()*** API. This API will remove the existing geofences.
```c# c# theme={null}
using MoEngage;
MoEngageGeofenceClient.StopGeofenceMonitoring();
```
# Limitations
Source: https://moengage.com/docs/developer-guide/unity-sdk/sdk-integration/limitations
Review unsupported features and native integration requirements when using the MoEngage Unity plugin.
Compared to the Native Android/iOS SDKs there are a certain set of features we either do not support or require native Android/iOS implementation when using our Unity plugin.
# Features not supported
* Action Buttons in iOS Notifications
* Huawei Push Kit
* Xiaomi Push
# Features that are supported but require Native Integration
* Inbox Module
* Cards
# Android
Source: https://moengage.com/docs/developer-guide/unity-sdk/sdk-integration/sdk-initialization/file-based-initialization/android
Use file-based initialization to configure the MoEngage SDK in your Unity project's Android library plugin.
## Overview
As an alternative to programmatic initialization, you can initialize the SDK using a configuration file. This lets you manage your Workspace ID and data center directly within a native configuration file, keeping them separate from your application logic.
If you require programmatic initialization instead, refer to the guide on [Manual Initialization](/docs/developer-guide/unity-sdk/sdk-integration/sdk-initialization/manual-initialization/android).
## Add Configuration File
Create `moengage_init_config.xml` in your Android library plugin at `Assets/Plugins/Android/.androidlib/res/values/moengage_init_config.xml` and specify your workspace ID and data center:
```xml moengage_init_config.xml theme={null}
YOUR_WORKSPACE_IDDATA_CENTER_X
```
For more information on creating an Android library plugin, refer to the [Unity documentation](https://docs.unity3d.com/6000.4/Documentation/Manual/android-library-plugin-create.html).
You must also call `MoEInitializer.initialiseDefaultInstance(application)` in your Application class alongside the file-based config. When this file is present, the SDK reads configuration from it at startup. Programmatic initialization takes precedence if both are provided.
Following details of the different data centers you need to set based on the dashboard hosts
| Data Center | Dashboard host |
| -------------------------- | ------------------------- |
| DataCenter.DATA\_CENTER\_1 | dashboard-01.moengage.com |
| DataCenter.DATA\_CENTER\_2 | dashboard-02.moengage.com |
| DataCenter.DATA\_CENTER\_3 | dashboard-03.moengage.com |
| DataCenter.DATA\_CENTER\_4 | dashboard-04.moengage.com |
| DataCenter.DATA\_CENTER\_5 | dashboard-05.moengage.com |
For more information about the detailed list of possible configurations, refer to the [API reference](https://moengage.github.io/android-api-reference/core/com.moengage.core/\[android-jvm]-mo-engage/-builder/index.html).
All the configurations are added to the builder before initialization. If you are calling initialize at multiple places, ensure that all the required flags and configurations are set each time you initialize to maintain consistency in behavior.
## Exclude MoEngage Storage File from Auto-Backup
This is a mandatory integration step and is very essential to prevent your data from getting corrupted. Android's auto back-up service periodically backs up Shared Preference files, Database files, etc, more details [here](https://developer.android.com/guide/topics/data/autobackup). This backup results in MoEngage SDK's identifiers being backed up and restored after re-install.This restoration of the identifier results in your data being corrupted and users not being reachable via push notifications.
To ensure data is not corrupted after a backup is restored, opt out of MoEngage SDK's storage files. Refer to [Exclude MoEngage Storage File from the Auto-Backup](/docs/developer-guide/android-sdk/sdk-integration/basic-integration/exclude-mo-engage-storage-file-from-auto-backup) section of the documentation to learn more about which files to exclude.
# iOS
Source: https://moengage.com/docs/developer-guide/unity-sdk/sdk-integration/sdk-initialization/file-based-initialization/ios
Initialize the MoEngage SDK in your Unity project's iOS configuration file with your workspace ID.
Unity iOS SDK supports file-based initialization only, using the `MoEngage-Info.plist` configuration file described below.
## Include MoEngage Configuration
After importing the MoEngage package, add a **MoEngage-Info.plist** file to your `Assets` folder (`Asset > Import New Asset`).
Starting from Unity SDK Core 6.0.0 version, support for `MoEngageConfiguration.h` is removed. You must migrate to `MoEngage-Info.plist` for SDK configuration.
In your `MoEngage-Info.plist`, set the workspace ID, region, and other configuration values:
```xml XML theme={null}
APPLICATION_IDYOUR_WORKSPACE_IDDATA_CENTERDATA_CENTER_01ENABLE_LOGSUNITY_CONTROLLER_SWIZZLING_ENABLEDANALYTICS_PERIODIC_FLUSH_DURATION60ANALYTICS_DISABLE_PERIODIC_FLUSHSTORAGE_ENCRYPTION_ENABLEDKEYCHAIN_GROUP_NAMENETWORK_ENCRYPTION_ENABLEDSHOULD_PROVIDE_DEEPLINK_CALLBACK
```
Set `SHOULD_PROVIDE_DEEPLINK_CALLBACK` to `true` if your app handles deeplink routing manually instead of letting the SDK handle navigation.
## Unity App Controller Swizzling
There are two approaches using which the SDK can be initialized:
1. **Unity App Controller Subclass:**\
SDK contains the `MoEUnityAppController` class which is the subclass of UnityAppController. Here we get a callback on AppLaunch to initialize MoEngage SDK.
2. **UnityAppController swizzling:**\
Set `UNITY_CONTROLLER_SWIZZLING_ENABLED` to `true` in `MoEngage-Info.plist` to enable UnityAppController swizzling. Here `application:didfinishLaunchinWithOptions:` method is swizzled to initialize MoEngage SDK.
UnityAppController swizzling (2nd approach) will be required if your project has multiple implementations of UnityAppController subclasses (1st approach), here the subclass defined in the plugin might not work reliably because here only one of the subclasses in the project will get the callbacks.
## Build and Replace
Once required packages are imported go to `File > Build Settings`, switch to iOS platform, and click on `Build`. Select `Replace` in the pop-up. It's necessary to select **Replace** on first-time integration, this is because we will be updating the build settings of the Xcode project for MoEngage SDK and also installing the native dependencies.
### CocoaPods
Make sure [CocoaPods](https://cocoapods.org/) is installed in your system before proceeding.
The SDK uses CocoaPods by default to manage native iOS dependencies. After building from Unity, open `Unity-iPhone.xcworkspace` in Xcode to run the app.
### Swift Package Manager
As an alternative to CocoaPods, you can use Swift Package Manager (SPM) to manage the MoEngage iOS dependency:
1. In Xcode, go to **File → Add Package Dependencies**.
2. Enter the MoEngage iOS SDK repository URL and select the required version.
3. Add the `MoEngageSDK` package to the `Unity-iPhone` target.
4. Remove the generated `Podfile` and close the `.xcworkspace`. Open `Unity-iPhone.xcodeproj` directly when using SPM.
# Android
Source: https://moengage.com/docs/developer-guide/unity-sdk/sdk-integration/sdk-initialization/manual-initialization/android
Manually initialize the MoEngage SDK in your Unity project's Android Application class.
## Enable Custom Manifest File
Navigate to Build Settings and switch the platform to Android. Then go to **Player Settings --> Player --> Android --> Publishing Settings**.\
Under the Build Heading check **Custom Main Manifest**.
## Adding Dependencies
MoEngage plugin depends on the following Jetpack libraries. In case you don't have them in your application already, please add them.\
You can choose to enable the custom gradle template in your Application's **Player Settings** and the below dependencies.
```Groovy Groovy theme={null}
implementation("androidx.core:core:1.9.0") implementation("androidx.appcompat:appcompat:1.4.2") implementation("androidx.lifecycle:lifecycle-process:2.5.1")
```
## Add Application Class
* Create a Java/Kotlin class and add it to the **Assets --> Plugins --> Android** folder. Extend the class with **android.app.Application** and override the onCreate().
* Declare the above-created class in the Manifest file inside the application tag.
```AndroidManifest.xml AndroidManifest.xml theme={null}
```
The Manifest file can be found in the **Assets--> Plugins --> Android** folder.
## SDK Initialization
Get the Workspace ID from **Dashboard → Settings → Workspace → General** on the MoEngage dashboard and initialize the MoEngage SDK in the **Application class's onCreate()** method.
It is recommended that you initialize the SDK on the main thread inside **onCreate()** and not create a worker thread and initialize the SDK on that thread.
```kotlin Kotlin theme={null}
import com.moengage.unity.wrapper.MoEInitializer
import com.moengage.core.MoEngage
import com.moengage.core.DataCenter
// "YOUR_WORKSPACE_ID" is the Workspace ID from the dashboard.
val moEngage = MoEngage.Builder(this, "YOUR_WORKSPACE_ID", DataCenter.DATA_CENTER_X)
MoEInitializer.initialiseDefaultInstance(applicationContext, moEngage)
```
```java Java theme={null}
import com.moengage.unity.wrapper.MoEInitializer;
import com.moengage.core.MoEngage;
import com.moengage.core.DataCenter;
// "YOUR_WORKSPACE_ID" is the Workspace ID from the dashboard.
MoEngage.Builder moEngage = new MoEngage.Builder(this, "YOUR_WORKSPACE_ID", DataCenter.DATA_CENTER_X);
MoEInitializer.INSTANCE.initialiseDefaultInstance(applicationContext, moEngage);
```
Following details of the different data centers you need to set based on the dashboard hosts
| Data Center | Dashboard host |
| -------------------------- | ------------------------- |
| DataCenter.DATA\_CENTER\_1 | dashboard-01.moengage.com |
| DataCenter.DATA\_CENTER\_2 | dashboard-02.moengage.com |
| DataCenter.DATA\_CENTER\_3 | dashboard-03.moengage.com |
| DataCenter.DATA\_CENTER\_4 | dashboard-04.moengage.com |
| DataCenter.DATA\_CENTER\_5 | dashboard-05.moengage.com |
For more information about the detailed list of possible configurations, refer to the [API reference](https://moengage.github.io/android-api-reference/core/com.moengage.core/\[android-jvm]-mo-engage/-builder/index.html).
All the configurations are added to the builder before initialization. If you are calling initialize at multiple places, ensure that all the required flags and configurations are set each time you initialize to maintain consistency in behavior.
You can also initialize the SDK using a configuration file instead of this programmatic approach. Refer to [File Based Initialization](/docs/developer-guide/unity-sdk/sdk-integration/sdk-initialization/file-based-initialization/android) for more information.
## Exclude MoEngage Storage File from Auto-Backup
This is a mandatory integration step and is very essential to prevent your data from getting corrupted. Android's auto back-up service periodically backs up Shared Preference files, Database files, etc, more details [here](https://developer.android.com/guide/topics/data/autobackup). This backup results in MoEngage SDK's identifiers being backed up and restored after re-install.This restoration of the identifier results in your data being corrupted and users not being reachable via push notifications.
To ensure data is not corrupted after a backup is restored, opt out of MoEngage SDK's storage files. Refer to [Exclude MoEngage Storage File from the Auto-Backup](/docs/developer-guide/android-sdk/sdk-integration/basic-integration/exclude-mo-engage-storage-file-from-auto-backup) section of the documentation to learn more about which files to exclude.
# SDK Initialization
Source: https://moengage.com/docs/developer-guide/unity-sdk/sdk-integration/sdk-initialization/sdk-initialization
Attach the MoEGameObject script to your Unity scene and configure your MoEngage App ID.
# Unity Initialization
In your Unity project, add an empty game object to the first scene, attach the ***MoEGameObject.cs*** script, and set the ***App ID*** as shown below
\
MoEngage supports two ways to initialize the SDK: manually in code, or through a native configuration file.
Unity iOS SDK supports file-based initialization only. Manual initialization is available for Android only.
## Manual Initialization
Initialize the SDK programmatically in your Android Application class.
* [Android](/docs/developer-guide/unity-sdk/sdk-integration/sdk-initialization/manual-initialization/android)
## File Based Initialization
Initialize the SDK using a native configuration file, keeping the Workspace ID and settings separate from your application logic.
* [Android](/docs/developer-guide/unity-sdk/sdk-integration/sdk-initialization/file-based-initialization/android)
* [iOS](/docs/developer-guide/unity-sdk/sdk-integration/sdk-initialization/file-based-initialization/ios)
# SDK Installation
Source: https://moengage.com/docs/developer-guide/unity-sdk/sdk-integration/sdk-installation/sdk-installation
Download and import the MoEngage Unity package into your project using the Unity Editor.
Connect your IDE to the [MoEngage docs MCP server](/docs/developer-guide/connect-your-ide-to-moengage-docs) for accurate, context-aware SDK guidance inside your development environment. Your assistant searches this documentation directly, helping you move through the integration with confidence.
The MoEngage Unity package requires Unity 6.4 (Hub version 6000.4.1f1) or above.
Review the [SDK Deprecation Policy](/docs/developer-guide/sdk-lifecycle-and-policies/sdk-deprecation-policy) for supported SDK versions and deprecation timelines before integrating.
## Build Environment Requirements
Ensure your build environment meets the following minimum requirements before integrating the SDK.
| Requirement | Minimum Version |
| ----------- | --------------- |
| AGP | 8.10.0 |
| Gradle | 8.13 |
| Min SDK | 25 |
| Target SDK | 35 or 36 |
## External Dependency Manager
The plugin uses the [External Dependency Manager](https://github.com/googlesamples/unity-jar-resolver) by Google for Unity (EDM4U) (formerly Play Services Resolver / Jar Resolver). Make sure to include it in your project before proceeding with the SDK Integration.
## Add MoEngage Package
To integrate the MoEngage Unity SDK, first, download the Unity packages from [here](https://github.com/moengage/MoEngage-Unity-SDK/releases). Then go to Unity Editor, navigate to `Assets > Import Package > Custom Package` , and import the `MoEngage.unitypackage`. Select all the files as shown below and click `Import`:
Once the plugin is integrated, head over to the SDK Initialization doc for setting up the Unity Project.
# Troubleshooting and FAQs - Unity
Source: https://moengage.com/docs/developer-guide/unity-sdk/troubleshooting-and-faqs/troubleshooting-and-faqs-unity
Find answers to common MoEngage Unity SDK questions and Android-specific troubleshooting steps.
## What is MoEDebuggerActivity?
The MoEngage SDK bundles the native MoEngage Android SDK, so your Android build includes `MoEDebuggerActivity`, a component that supports on-device SDK debugging. Refer to [What is MoEDebuggerActivity?](/docs/developer-guide/android-sdk/troubleshooting-and-faqs/troubleshooting-and-faqs) to understand what it does.
To remove it from your app, add the following to your Android project's `AndroidManifest.xml`:
```xml theme={null}
```
# Cards
Source: https://moengage.com/docs/developer-guide/web-sdk/cards/cards
Set up MoEngage Cards to deliver targeted inbox and newsfeed messages on your website.
Cards can be used to create targeted or automated App Inbox/NewsFeed messages that can be grouped into various categories and target users with different updates/offers that can stay in the Inbox/Feed over a designated period. For more information, refer to [Cards](https://www.moengage.com/docs/user-guide/campaigns-and-channels/cards/create/create-a-card-campaign).
## SDK Installation
Pass the Cards config in the SDK [initialization](https://www.moengage.com/docs/developer-guide/web-sdk/web-sdk-integration/basic-integration/web-sdk-integration) script as shown below:
```javascript JavaScript lines wrap theme={null}
Moengage = moe({
appId: moeAppID,
env: 'LIVE',
logLevel: 0,
cards: {
enable: true,
placeholder: '#cardIcon' // CSS selector of inbox icon
}
});
```
`placeholder` is the icon on click of which the inbox will open. Make sure it is provided by you and it exist in the DOM.
## UI Customizations
SDK provides a set of UI customizations that can overwrite the default values. Refer to the code snippet below for the UI customizations.
```javascript JavaScript lines wrap theme={null}
Moengage = moe({
appId: moeAppID,
env: 'LIVE',
logLevel: 0,
cards: {
enable: true,
placeholder: "#cardIcon", // CSS selector of inbox icon
backgroundColor: "#F6FBFC", // any valid CSS color format
overLayColor: "rgba(0, 0, 0, 0.8)",
fontFaces: [{
family: "Sofia",
url: "https://fonts.gstatic.com/s/sofia/v14/8QIHdirahM3j_su5uI0Orbjl.woff2"
}, {
family: "Audiowide",
url: "https://fonts.gstatic.com/s/audiowide/v16/l7gdbjpo0cum0ckerWCdlg_OMRlABg.woff2"
}],
cardDismiss: {
color: "#db2828", // any valid CSS color format
enable: false // boolean value, which enable the dismiss option.
},
optionButtonColor: "#C4C4C4", // any valid CSS color format
dateTimeColor: "#8E8E8E", // any valid CSS color format
unclickedCardIndicatorColor: "blue", // any valid CSS color format
pinIcon: "https://app-cdn.moengage.com/sdk/pin-icon.svg", // absolute path to the icon image.
refreshIcon: "https://app-cdn.moengage.com/sdk/refresh-icon.svg", // absolute path to the icon image.
navigationBar: {
backgroundColor: "#00237C", // any valid CSS color format
text: "Notifications", // string. eg, Notifications
color: "#fff", // any valid CSS color format
fontSize: "16px", // any valid CSS size format
fontFamily: "", // any font family which is added to the website
},
closeButton: {
webIcon: "https://app-cdn.moengage.com/sdk/cross-icon.svg",
mWebIcon: "https://app-cdn.moengage.com/sdk/cross-icon.svg",
},
tab: {
active: {
color: "#06A6B7",
underlineColor: "#06A6B7",
backgroundColor: "transparent"
},
inactiveTabFontColor: "#7C7C7C",
fontSize: "14px", // any valid CSS size format
fontFamily: "", // any font family which is added to the website
backgroundColor: "#fff", // any valid CSS color format
},
webFloating: {
enable: false, // false by default
icon: "https://app-cdn.moengage.com/sdk/bell-icon.svg", // absolute path to the icon image. by default, our icon will be used.
postion: "0px 10px 40px 0", // need all 4 offset in proper CSS format in the order of top, right, bottom, left.
countBackgroundColor: "#FF5A5F",
countColor: "#fff",
iconBackgroundColor: "#D9DFED",
fontFamily: "Audiowide"
},
mWebFloating: {
enable: false, // false by default
icon: "https://app-cdn.moengage.com/sdk/bell-icon.svg", // absolute path to the icon image. by default, our icon will be used.
postion: "0px 10px 40px 0", // need all 4 offset in proper CSS format in the order of top, right, bottom, left.
countBackgroundColor: "#FF5A5F",
countColor: "#fff",
iconBackgroundColor: "#D9DFED",
fontFamily: "Audiowide"
},
card: {
headerFontSize: "16px",
descriptionFontSize: "14px",
ctaFontSize: "12px",
fontFamily: "inherit",
horizontalRowColor: "#D9DFED"
},
noDataContent: {
img: "https://app-cdn.moengage.com/sdk/cards-no-result.svg",
text: "No notifications to show, check again later.",
}
}
});
```
## APIs
### Unclicked Count
The SDK provides an API to fetch the number of cards that haven't been clicked by the users. To get the count, use the following API.
```javascript JavaScript lines wrap theme={null}
```
### New Card Count
The SDK provides an API to get the new cards for the user on the device. To get the count, use the following API.
```javascript JavaScript lines wrap theme={null}
```
## Callbacks
The SDK provides the following callbacks:
### Inbox Open Callback
To get a callback when the user clicks on the inbox icon and when the inbox is open, register the callback function to the setInboxOpenListener() method as shown below.
```javascript JavaScript lines wrap theme={null}
```
### Inbox Close Callback
To get a callback when the user closes the inbox, register the callback function to the setInboxCloseListener() method as shown below.
```javascript JavaScript lines wrap theme={null}
```
### Card Click Callback
To get a callback when the user clicks on a card, register the callback function to
setCardClickListener() method:
```javascript JavaScript lines wrap theme={null}
```
# Self Handled Cards
Source: https://moengage.com/docs/developer-guide/web-sdk/cards/self-handled-cards
Fetch card campaign data via the MoEngage SDK and build custom card UI on your website.
Self-handled cards allow you to create Card Campaigns on the MoEngage Platform and display the cards anywhere inside the website. SDK provides APIs to fetch campaign data, which you can use to create your view for cards.
## SDK Installation
Pass the Cards config in the SDK [initialization](/docs/developer-guide/web-sdk/web-sdk-integration/basic-integration/web-sdk-integration) script, as shown below.
```javascript JavaScript lines wrap theme={null}
Moengage = moe({
appId: moeAppID,
env: 'LIVE',
logLevel: 0,
cards: {
enable: true
}
});
```
Cards is a separate module and it gets loaded asynchronously with core SDK module. So it may not be available immediately on the page load.
Please use this event listener and call the Cards API once it has been initialised:
```javascript JavaScript lines wrap theme={null}
window.addEventListener('MOE_LIFECYCLE', event => {
if (event.detail.name === 'CARDS_INITIALIZED') {
// call the cards APIs here
}
});
```
If you are using npm package, then use this helper function:
```javascript JavaScript lines wrap theme={null}
moengage.onCardsLoaded().then(function() {
// call the cards APIs here
})
```
### Notify on Inbox Open
Whenever you open the inbox, you can notify Moengage as shown below to sync the data and track the inbox open event.
```javascript JavaScript lines wrap theme={null}
```
### Fetch Categories
To fetch all the categories for which cards are configured, use the getCardCategories() API.
```javascript JavaScript lines wrap theme={null}
```
Additionally, you can have an **All** category which would be a superset of all the categories. Use the isAllCategoryEnabled() API.
```javascript JavaScript lines wrap theme={null}
```
### Fetch Cards for Categories
To fetch the cards eligible for display for a specific category, use the getCardsForCategory(categoryName) API.
```javascript JavaScript lines wrap theme={null}
```
To fetch all the cards eligible for display irrespective of the category, pass the category 'All' as shown below
```javascript JavaScript lines wrap theme={null}
```
### Fetch Card Info
Instead of using separate APIs to fetch Cards and categories, you can use the \*getCardsInfo(cardID)\*API to fetch all the information in one go.
```javascript JavaScript lines wrap theme={null}
```
### Refresh Cards from the Server
Use the fetchCards() API to refresh cards from the MoEngage server if required.
```javascript JavaScript lines wrap theme={null}
```
For details on the sync timing and rate limits for `fetchCards()`, see [When Does the MoEngage SDK Sync Card Data?](/docs/user-guide/campaigns-and-channels/cards/faqs-cards/when-does-the-moengage-sdk-sync-card-data)
### Track Statistics for Cards
Since the UI/display of the cards is controlled by the application, to track the statistics on display and click, we need the application to notify the SDK.
#### Impressions
Call the \*cardShown(cardID)\*API when a specific card is visible on the screen.
```javascript JavaScript lines wrap theme={null}
```
#### Clicks
Whenever a user clicks on a card, call the *cardClicked(cardID, widgetID)* API and pass the card object widget identifier for the UI element clicked.
```javascript JavaScript lines wrap theme={null}
```
#### Delete Card
Call the *deleteCard(cardID)* API to delete a card.
```javascript JavaScript lines wrap theme={null}
```
# Configure Data opt-out in Web SDK
Source: https://moengage.com/docs/developer-guide/web-sdk/data-tracking/configure-data-opt-out-in-web-sdk
Disable user data tracking in the MoEngage Web SDK to comply with GDPR and CCPA regulations.
To comply with privacy regulations such as GDPR and CCPA, your application must provide users with the ability to opt out of data tracking. The MoEngage Web SDK provides APIs to disable user-specific data collection while maintaining core functionality and anonymous analytics.
* By default, data tracking is enabled.
* When a user opts out, the SDK stops collecting personally identifiable information (PII) and behavior-specific data.
## How Data Tracking Opt-out Works
When you disable data tracking, the SDK performs the following actions to ensure compliance and privacy:
* **Data Erasure (Local):** Deletes existing user attributes, events, session information, and batches from local storage.
* **State Persistence:** Maintains the opt-out preference across browser sessions and user logouts.
* **Anonymous Tracking:** Continues to track a limited set of "Permitted Events" (like Web Push subscriptions) without associating them with a specific user identity.
* **Cross-Subdomain Support:** Synchronizes the opt-out state across subdomains (if configured).
**Server Data Notice**
Data erasure occurs only locally within the browser's storage. Disabling data tracking does not retroactively delete historical data that has already been sent to and stored on MoEngage servers.
## Disable Data Tracking
Call the `disableDataTracking()` method when a user rejects tracking cookies or requests to opt out.
```javascript JavaScript lines wrap theme={null}
Moengage.disableDataTracking();
```
### What happens when disabled?
* New custom events and user attributes are not tracked.
* PII (Email, Mobile Number, Name, etc.) is blocked from being sent to the server.
* The SDK logs an error if you attempt to call `disableDataTracking()` when it is already disabled.
* If the SDK itself is disabled via `disableSdk()`, calling `disableDataTracking()` will have no effect.
## Enable Data Tracking
Call the `enableDataTracking()` method when a user provides consent to be tracked.
```javascript JavaScript lines wrap theme={null}
Moengage.enableDataTracking();
```
### Re-identifying Users after Consent
After enabling data tracking, you must call `identifyUser()` to associate the current session and any prior anonymous events with a specific user profile.
```javascript JavaScript lines wrap theme={null}
Moengage.enableDataTracking().then(() = {
// Re-identify user to link anonymous events to the profile
Moengage.identifyUser("USER_UNIQUE_ID");
})
```
## Supported Platforms
The Data Tracking Opt-out feature is currently supported on the following platforms and frameworks:
| Platform / Framework | Support Status |
| ------------------------ | --------------- |
| Native Web SDK | ✅ Supported |
| NPM Module | ✅ Supported |
| Google Tag Manager (GTM) | ✅ Supported |
| Shopify | ✅ Supported |
| Flutter | ✅ Supported |
| Segment / VTEX / AMP | ❌ Not Supported |
## Data Collection Behavior
### Permitted Events
When data tracking is disabled, the SDK tracks standard events anonymously to maintain core functionality. For a full list of these interactions, refer to the [Standard Events documentation](https://www.moengage.com/docs/user-guide/data/event-data/derived-events-and-attributes). All standard events are tracked except for custom events and user attributes, which are restricted as listed below.
### Restricted Data (PII)
**Restricted Attributes**
* Personal Identifiers: First Name, Last Name, Full Name.
* Contact Info: Email ID, Mobile Number.
* Demographics: Gender, Birth Date.
* All custom user attributes.
* All custom events.
## Frequently Asked Questions
### Does the opt-out state persist after a user logs out?
Yes. The tracking preference is stored in the browser's storage and persists across app sessions and user logouts to ensure user privacy choices are respected.
### Will I still see page view counts for opted-out users?
Yes. **Viewed Web Page** is a permitted event. However, these views are recorded anonymously and are not linked to a specific user profile or historical data.
### Can I use this alongside disableSdk()?
`disableSdk()` takes precedence. If the SDK is disabled, SDK does not perform any operations. `disableDataTracking()` allows the SDK to remain active for anonymous features while stopping personalized data collection.
# Setting Unique Id for SDK versions below 2.52.2
Source: https://moengage.com/docs/developer-guide/web-sdk/data-tracking/setting-unique-id-for-sdk-versions-below-2-52-2
Set a unique user ID for login and logout in MoEngage Web SDK versions below 2.52.2.
## Implementing Login/Logout
* It's important to set the ID when a user logs into your app.
* This merges the new user with the existing user, if any exists, and will help prevent the creation of unnecessary/stale users.
* Setting the ID is a critical piece to tie a user across devices as well across all platforms (i.e. iOS, Android, Web). Call the login method as soon as the user is logged in. ID can be something like an email ID, a username (unique), or a database ID or any Backend generated ID.
* Do not set this for the user who not logged in.
## Log In
```javascript JavaScript lines wrap theme={null}
Moengage.add_unique_user_id(UNIQUE_ID); // UNIQUE_ID is used to uniquely identify a user.
```
**Warning**
If you do not use the MoEngage logout method and call `Moengage.add_unique_user_id(NEW_UNIQUE_ID)`, then the SDK will trigger MoEngage force-logout. A new user profile will get created with `NEW_UNIQUE_ID` as its ID and the first user profile will receive the logout event. This way of using `Moengage.add_unique_user_id` is NOT recommended as new user creation resets the current user and creates unintended users in our system. You should always explicitly call the MoEngage logout method before calling this method again.
**Critical - Very Important Integration Guideline**
Never use both the login methods - `identifyUser` (login method of SDK versions 2.52.2 onwards) and `add_unique_user_id` in your project. Use only either one of the methods. Using both the methods can lead to inconsistent user profile creation and merging in your MoEngage account.
## Log Out
Logs out the current user.
```javascript JavaScript lines wrap theme={null}
Moengage.destroy_session();
```
## Update User
```javascript JavaScript lines wrap theme={null}
Moengage.update_unique_user_id(NEW_UNIQUE_ID);
```
**Critical**
All the methods mentioned above- `add_unique_user_id`, `destroy_session` and `update_unique_user_id` should be called in proper order. So make sure, you track other attributes after these methods are executed completely. Since it returns a promise, you can use. For example:
```javascript JavaScript lines wrap theme={null}
Moengage.add_unique_user_id(UNIQUE_ID).then(() => {
Moengage.add_mobile('7777777777')
});
```
# Web SDK Data Tracking Introduction
Source: https://moengage.com/docs/developer-guide/web-sdk/data-tracking/web-sdk-data-tracking-introduction
Learn about user attribute and event tracking capabilities in the MoEngage Web SDK.
# Overview
MoEngage Web SDK, by default, tracks information for MoEngage default attributes and events. Use additional tracking for best personalization of the message and advance segmentation. Additional tracking is done either for:
# User Attributes
Persistent forever.
For example, email, first name, last name and so on. For more information, refer to [User Attributes](https://www.moengage.com/docs/developer-guide/web-sdk/data-tracking/web-sdk-user-attributes-tracking).
**Note**
You can not use "moe\_" as a prefix while naming events, event attributes, or user attributes. It is a system prefix and using it might result in periodic blacklisting without prior communication.
# Events
Persistent for 60 days unless otherwise defined in your contract.
For example, Order Successful, Started Application and so on.
For more information on MoEngage tracked attributes and events, refer to [Events and Attributes](https://www.moengage.com/docs/user-guide/data/event-data/derived-events-and-attributes) and [Events Tracking](https://www.moengage.com/docs/developer-guide/web-sdk/data-tracking/web-sdk-events-tracking).
For further assistance, please contact your MoEngage Customer Success Manager (CSM) or the Support team.
# Web SDK Events Tracking
Source: https://moengage.com/docs/developer-guide/web-sdk/data-tracking/web-sdk-events-tracking
Track user actions and event attributes on your website using the MoEngage Web SDK.
* MoEngage highly recommends using the optional step for MoEngage Web SDK integration.
* SDK adheres to the MoEngage FUP policies. For more information, refer to the [Fair Usage Policy](https://www.moengage.com/docs/user-guide/data/key-concepts/fair-usage-policy-fup).
## Track Event
Tracking events records the user actions and the action properties. A single user action is recorded with every track\_event call. MoEngage recommends that you make your event names human-readable so that everyone in your team can know what they mean instantly.
You can track an event using track\_event with the event name and the event characteristics such as attributes or properties.
```javascript JavaScript lines wrap theme={null}
Moengage.trackEvent("EVENT_NAME_1"); // This event has no attributes
// Track events with additional attributes
Moengage.trackEvent("EVENT_NAME_2", {
"attribute_1": "value_1", // string value
"attribute_2": 2, // numeric value
"attribute_3": 3.4, // numeric value
"attribute_4": new Date(2017, 0, 31), // datetime value. Example value represents 31 January, 2017.
});
```
Add all the additional information which you think would be useful for segmentation while creating campaigns. For example, the following code tracks a purchase event of a product. We are including attributes like amount, quantity, category, which describe the event we are tracking.
```javascript JavaScript lines wrap theme={null}
Moengage.trackEvent("Purchase", {
"quantity":2,
"product_name":"Ipad mini",
"price": 599.99,
"currency": "USD"
});
```
**Critical**
* Ensure that you are tracking event and user attributes without changing their data types. For instance, in the above purchase event, amount and quantity are tracked in numeric form. MoEngage detects the data type automatically unless you explicitly specify it as a string.
* Having unique timestamps for events is crucial to maintain accurate and reliable data. Please ensure that events do not share the same timestamp down to the millisecond level.
### Event tracking using Google Tag Manager (GTM)
Use the described code in the `Custom HTML Tag` inside GTM. This Tag should be fired once per event and triggered on the elements where you wish to track website events. The event attributes can be picked up from GTM Data Layer.
```JavaScript JavaScript lines wrap theme={null}
```
## Non-Interactive Event
Events that do not affect the session duration calculation in MoEngage Analytics are marked as Non-Interactive events.
The following are considered non-interactive events:
* Do not start a new session, even when the website is in the foreground
* Do not extend the session
* Do not have information on source and session
For example, events that are tracked when the website is in the background, are not initiated by users and hence are marked as non-interactive.
To mark an event as non-interactive send `moe_non_interactive: 1` while tracking the event as described:
```javascript JavaScript lines wrap theme={null}
Moengage.trackEvent("Content Refreshed", {"NewsCategory": "Politics", "moe_non_interactive": 1})
```
## Callbacks on events tracked by SDK
There are few events which are tracked by SDK automatically for you and if you want to get those, then you can listen to the events as follows:
```JavaScript JavaScript lines wrap theme={null}
window.addEventListener('MOE_AUTOMATED_EVENTS', function (event) {
switch (event.detail.name) {
case 'MOE_WEB_UNSUBSCRIBED':
console.log(event.detail.data); // it contains all the attributes of the event
break;
case 'EVENT_ACTION_WEB_SESSION_START':
console.log(event.detail.data); // it contains all the attributes of the event
break;
}
});
```
### List of events
1. MOE\_WEB\_UNSUBSCRIBED
2. EVENT\_ACTION\_WEB\_SESSION\_START
3. MOE\_PAGE\_VIEWED
4. MOE\_WEB\_OPTIN\_BANNER\_LOAD
5. MOE\_WEB\_OPTIN\_CLOSED
6. MOE\_WEB\_OPTIN\_ACCEPTED
7. MOE\_USER\_SUBSCRIBED
8. MOE\_OPT\_IN\_SHOWN
9. MOE\_OPT\_IN\_ALLOWED
10. MOE\_OPT\_IN\_DISMISSED
11. MOE\_OPT\_IN\_BLOCKED
12. MOE\_LOGOUT
13. MOE\_ONSITE\_MESSAGE\_CLICKED
14. MOE\_ONSITE\_MESSAGE\_SHOWN
15. MOE\_ONSITE\_MESSAGE\_DISMISSED
16. MOE\_ONSITE\_MESSAGE\_AUTO\_DISMISS
## Troubleshooting
### Event is not tracked on redirection when I am using window\.open(..)
In case you are redirecting to another page using `window.open` method and also tracking some event(s) on redirection then you need to wrap the `window.open` in a `setTimeout`. eg-
```javascript JavaScript lines wrap theme={null}
setTimeout(() => window.open(), 0);
```
### Events are not getting tracked when the page URL is very big
URL is tracked by default in all the events as an attribute. But the maximum length of the URL supported is 512 character. If the URL length exceeds that, then the whole events will be dropped.
So make sure the URL length does not exceed 512 characters
### Page Viewed event is not getting tracked on back/forward navigation
Your website might be eligible for bfcache. Back/forward cache (or bfcache) is a browser optimization that enables instant back and forward navigation. bfcache is an in-memory cache that stores a complete snapshot of a page (including the JavaScript heap) as the user is navigating away. Read more about bfcache [here](https://web.dev/articles/bfcache)
Thus the Moengage SDK does not re-initialises again and hence the page viewed events are not tracked.
To include bfcache restores in your pageview count, set listeners for the `pageshow` event and check the `persisted` property.
```javascript JavaScript lines wrap theme={null}
window.addEventListener('pageshow', (event) = {
// Send another pageview if the page is restored from bfcache.
if (event.persisted) {
Moengage.trackPageView();
}
});
```
For further assistance, please contact your MoEngage Customer Success Manager (CSM) or the Support team.
# Web SDK User Attributes Tracking
Source: https://moengage.com/docs/developer-guide/web-sdk/data-tracking/web-sdk-user-attributes-tracking
Track user attributes like email, name, and gender on your website using the MoEngage Web SDK.
MoEngage highly recommends using the optional step for MoEngage Web SDK integration.
User attributes are specific traits of a user such as an email, username, mobile, gender and so on. User Attributes helps target users based on these attributes across devices or installs or to personalize the messages.
## Pre-defined Attributes
Use the following MoEngage methods to make use of the standard user attributes.
```javascript JavaScript lines wrap theme={null}
Moengage.setFirstName("Dominick");
Moengage.setLastName("Cobb");
Moengage.setEmailId("dom@level5.com");
Moengage.setMobileNumber("+12399999999");
Moengage.setUserName("Dominick (Dom) Cobb"); // Full name for user
Moengage.setGender("M");
Moengage.setBirthDate(new Date(1980, 2, 31));
```
## Custom Attributes
```javascript JavaScript lines wrap theme={null}
// string
Moengage.setUserAttribute("ATTRIBUTE_NAME_1", "value");
// Integer - Numeric
Moengage.setUserAttribute("ATTRIBUTE_NAME_2", 1);
// Double - Numeric
Moengage.setUserAttribute("ATTRIBUTE_NAME_3", 5.99);
// Date
Moengage.setUserAttribute("ATTRIBUTE_NAME_4", new Date(2021, 2, 10));
// Boolean
Moengage.setUserAttribute("ATTRIBUTE_NAME_5", false);
// Arrays can be only in string or in number format.
// Array (all items should be of same data type)
Moengage.setUserAttribute('colors', ['blue', 'green', 'red']);
// Array (all items should be of same data type)
Moengage.setUserAttribute('ids', [77892, 1123, 3311]);
// Object (upto 2 levels and maximum payload size of 150 KB is supported)
// The value of the highest-level key has to be of primitive JavaScript type or Date
Moengage.setUserAttribute('someCustAttr', { 'key1': { 'subKey1': 'someVal1', 'subKey2': true, 'subKey3': 125676, 'subKey4': new Date() }, 'key2': [1, 4, 5, 3] });
```
## User Login and Logout
ID is used to uniquely identify a user within the MoEngage dashboard.
Ensure log in and log out of users are implemented correctly during the visit to your website and users are authenticated.
If the user [log in](#log-in) and [log out](#log-out) is not handled correctly, user data may get corrupted.
When you go live with MoEngage web SDK for the first time, ensure that you are setting the ID of your existing website users on page load.
As soon as the user is authenticated, ensure that the user id is passed on to the MoEngage SDK using the login method. After the user logs out of your app, ensure to call the logout method of MoEngage.
Use the following MoEngage methods on user login, user log out and user update:
### Log In
For SDK versions below 2.52.2 refer to [this document](/docs/developer-guide/web-sdk/data-tracking/setting-unique-id-for-sdk-versions-below-2-52-2).
Please ensure that you are using the latest [integration script](https://www.moengage.com/docs/developer-guide/web-sdk/web-sdk-integration/basic-integration/web-sdk-integration) before using the below MoEngage methods.
```javascript JavaScript lines wrap theme={null}
/** if a single argument is passed into identifyUser method, it will be treated as ID */
Moengage.identifyUser(UNIQUE_ID); // UNIQUE_ID is used to uniquely identify a user
/** if email has been chosen as identity */
Moengage.identifyUser({ u_em: 'emailValue@emailDomain.com' });
/** if mobile has been chosen as identity */
Moengage.identifyUser({ u_mb: '7777777777' });
/** you can set two or more identities at the same time */
Moengage.identifyUser({ u_em: 'emailValue@emailDomain.com', u_mb: '7777777777', uid: 'unique_id_value' });
```
**Information**
Updates are made to SDK functions to improve user identification and session management.
* **Forced Logout**: The MoEngage SDK no longer automatically logs out the previous user when a new user is detected on the device. Logout should now be explicitly called for workspaces enabled with Identity resolution to avoid data corruption.
* **SetUniqueID**: *IdentifyUser* function supports multiple identifiers, which replaces the need of using *SetUniqueID* function for user identification. Note that *SetUniqueID* is marked for removal in the future releases of SDK versions - it is important to use *identifyUser* instead especially if you are using Identity resolution in your workspace.
* **SetAlias**: For workspaces with the Identity resolution feature enabled, MoEngage SDK stores the previous identifier values. When *IdentifyUser* function is used to track the new identifier values, MoEngage SDK detects the change in identifier value and reports accordingly.
* If you call the *IdentifyUser* function without logging out, then the existing logged-in user's ID is updated.
Refer to our help [document](/docs/user-guide/data/user-data/unified-identity-identity-resolution) to learn more about the feature.
Here, `u_em`, `u_mb`, `uid` are standard user attributes. Please refer to the below table to identify user with standard user attributes:
| User Attribute Name | Key name to be used in identifyUser method |
| :----------------------- | :----------------------------------------- |
| ID | uid |
| Email (Standard) | u\_em |
| Gender | u\_gd |
| Birthday | u\_bd |
| Name | u\_n |
| First Name | u\_fn |
| Last Name | u\_ln |
| Mobile Number (Standard) | u\_mb |
You can identify a user with custom user attributes as well if the same have been chosen as identities in your MoEngage dashboard.
```javascript JavaScript lines wrap theme={null}
/** replace custom_attribute_name with the actual name of your custom user attribute and attributeValue with the actual value you want to assign to the attribute */
Moengage.identifyUser({ custom_attribute_name: 'attributeValue' });
/** you can set two or more identities at the same time */
Moengage.identifyUser({ cust_attr_1: 'value1', cust_attr_2: 'value2' });
/** you can set custom user identity and standard user identity at the same time */
Moengage.identifyUser({ cust_attr_1: 'value1', cust_attr_2: 'value2', u_em: 'emailValue@emailDomain.com' });
```
If you want to retrieve the current state of identities set for the user, you can use the below method:
```javascript JavaScript lines wrap theme={null}
Moengage.getUserIdentities();
/**
returns an object with the user's current set identities and their values:
{ u_em: 'emailValue@emailDomain.com', u_mb: '7777777777', uid: 'valueOfID' }
*/
```
**Critical - Very Important Integration Guideline**
Never use both the login methods - `identifyUser` and `updateUniqueUserId` (login method of SDK versions below 2.52.2) in your project. Use only either one of the methods. Using both the methods can lead to inconsistent user profile creation and merging in your MoEngage account.
In order the get the value of a tracked user attributes, you can use the below method:
```javascript JavaScript lines wrap theme={null}
Moengage.getUserAttribute(ATTRIBUTE_NAME);
/**
Pass the attribute name as the paramaeter and it will return the value of the attribute if present.
Note: This method only returns locally tracked user attributes. if the user logs out and then logs in, the previously tracked attributes will not be fetched using this method.
*/
```
### Log Out
Logs out the current user.
```javascript JavaScript lines wrap theme={null}
Moengage.logoutUser();
```
### Update User
```javascript JavaScript lines wrap theme={null}
/** Set the ID for the first time */
Moengage.identifyUser(UNIQUE_ID);
/** Update the ID */
Moengage.identifyUser(NEW_UNIQUE_ID);
/** Set the identities for the first time */
Moengage.identifyUser({ uid: UNIQUE_ID, u_em: 'emailValue@emailDomain.com', u_mb: '7777777777' });
/** Update the identities - you can update one or more identities. */
Moengage.identifyUser({ u_em: 'updatedValue@emailDomain.com', u_mb: '8888888888' });
```
**Critical**
`identifyUser` and `logoutUser` methods should be called in proper order. So make sure, you track other attributes after these methods are executed completely. Since these return promise, you can track data like this:
```javascript JavaScript lines wrap theme={null}
Moengage.identifyUser({ u_em: 'emailValue@emailDomain.com', uid: UNIQUE_ID }).then(() => {
Moengage.setMobileNumber('7777777777')
});
```
## Track User Attributes
Ensure that you are tracking user attributes in the following cases:
1. When a new attribute is set for a user. For example, set the Email Id or Mobile No. attribute for the user after a user logs in or signs up on the website.
2. When the value of an existing attribute is updated.
3. When you go live for the first time with MoEngage web SDK integration, ensure that you are passing the user attributes set for your existing website users after page load (if they are not already sent to MoEngage).
4. For more information on supported data types and data tracking policies, please refer to [Data Tracking Policies](https://www.moengage.com/docs/user-guide/data/key-concepts/data-tracking-policies).
Make sure that you are not using a single unique id for all the users, this is possible only when the unique id value is hardcoded, instead of retrieving from your servers.
## Attribute tracking via Google Tag Manager (GTM)
You can follow the documentation at [Google Tag Manager(GTM)](https://partners.moengage.com/hc/en-us/articles/7191684558612) to track attribute via GTM.
## Tracking at the time of Redirection
In case of redirection to another page immediately after tracking attribute or events, make sure that the tracking is completed and then you redirect.
```javascript JavaScript lines wrap theme={null}
Moengage.identifyUser({ u_em: 'emailValue@emailDomain.com', uid: UNIQUE_ID }).then(() => {
window.open('[https://www.moengage.com](https://www.moengage.com)')
});
```
If you cannot wait for the tracking to complete and need to redirect immediately, then it is suggested to track the data on the next page after redirection.
## User Attribute Caching
Duplicate User Attributes won't be tracked for 6 hours.
For more information about the default attributes collected by MoEngage SDK, refer to [Web SDK Data Collection](https://www.moengage.com/docs/user-guide/getting-started/integration/default-web-sdk).
For further assistance, please contact your MoEngage Customer Success Manager (CSM) or the Support team.
# Web SDK Deprecated Versions
Source: https://moengage.com/docs/developer-guide/web-sdk/deprecated-versions/deprecated-versions
Check the support status of MoEngage Web SDK versions: which are current, which are still supported, and which are deprecated, along with deprecation dates.
Use this page to check the support status of MoEngage Web SDK versions. It lists which versions are current, which are still supported, and which are deprecated, along with deprecation dates. This is the source of truth for Web SDK version status. For how the lifecycle works and what deprecation means for your integration, see the [SDK Deprecation Policy](/docs/developer-guide/sdk-lifecycle-and-policies/sdk-deprecation-policy). To set up the Web SDK, see the [integration guide](/docs/developer-guide/web-sdk/web-sdk-overview/web-sdk-overview).
## Version Support Status
**Current** — The latest version. It receives new features, fixes, and support.
**Supported** — An older version within its support window. It continues to receive support.
**Deprecated** — A version outside its support window. It no longer receives fixes or support.
| Version | Status | Deprecation Date | Notes |
| ---------------- | ---------- | ---------------- | -------------------------------------------------------------------------------------------------------------- |
| 2.78.x | Current | TBD | Latest version. Receives new features, fixes, and support. |
| 2.56.1 to 2.77.x | Supported | TBD | Receives support. |
| 2.56.0 and below | Deprecated | August 2027 | Support, debugging, and fixes are no longer available. Data continues to flow. Upgrade to the current version. |
MoEngage supports each SDK version for 3 years from its initial release; deprecation then takes effect the following August. See the [SDK Deprecation Policy — Support Window](/docs/developer-guide/sdk-lifecycle-and-policies/sdk-deprecation-policy#support-window) for details. This table is updated when MoEngage announces further deprecations. To receive advance notice, subscribe to the SDK newsletter and ensure your workspace administrator email is current.
## Understanding Deprecated Status
A deprecated version continues to send and receive data. MoEngage does not block traffic. After the deprecation date, MoEngage no longer debugs, fixes, or supports issues specific to that version, and the resolution for any issue is to upgrade to the current version. All fixes ship in the current version only and are not available on supported or deprecated versions. For the complete policy, see the [SDK Deprecation Policy](/docs/developer-guide/sdk-lifecycle-and-policies/sdk-deprecation-policy).
## Upgrading to a Supported Version
* Refer to the [Web SDK release notes](/docs/release-notes/sdks/web) for the current version changes.
* For help, contact your MoEngage Customer Success Manager (CSM) or the Support team.
## Frequently Asked Questions
Sites on deprecated versions continue to run and data continues to flow. However, MoEngage no longer provides support, debugging, or fixes for deprecated versions. Upgrading to the current or a supported version using the [Web SDK release notes](/docs/release-notes/sdks/web) ensures you receive continued support.
MoEngage sends advance notice 6 to 12 months before the deprecation date through email to workspace administrators, the quarterly SDK newsletter, in-dashboard notices, and SDK release notes. See the [SDK Deprecation Policy](/docs/developer-guide/sdk-lifecycle-and-policies/sdk-deprecation-policy) for the full notification schedule.
# MoEngage Assist Chrome Extension
Source: https://moengage.com/docs/developer-guide/web-sdk/integration-validation/moengage-assist-chrome-extension
Install the MoEngage Assist Chrome extension to debug your Web SDK integration, inspect live event payloads, and troubleshoot campaigns.
# Overview
The MoEngage Assist Chrome extension is a dedicated debugging tool designed to help developers and marketers monitor and troubleshoot their MoEngage Web SDK integration and implementation. Install the extension to validate and debug your Web SDK integration directly from your browser.
Web SDK version [2.74.00](/docs/release-notes/sdks/web#7th-may-2026) or later is required to ensure complete compatibility and functionality of Web Assist.
With this extension, you can:
* Verify service worker registration.
* Confirm push token generation.
* Inspect event tracking in real time.
* Troubleshoot and debug implementation issues.
* Access campaigns and related features.
# Installation Steps
1. Navigate to the [Chrome Web Store](https://chromewebstore.google.com/).
2. Search for [*MoEngage Assist*](https://chromewebstore.google.com/detail/dhggnkfnnoebbfofpimfehcklnekmbgi?utm_source=item-share-cb).
3. Select **Add to Chrome** to install the extension.
4. Pin the extension to your browser toolbar for quick access.
If the MoEngage Web SDK is not installed on the active webpage, the extension displays an "SDK not found" state. For more information on integrating the Web SDK, see [Web SDK Integration](https://www.MoEngage.com/docs/developer-guide/web-sdk/web-sdk-integration/basic-integration/web-sdk-integration).
# Extension Tabs
When the Web SDK is successfully integrated, the extension populates operational data across the following four tabs.
## Issues
The **Issues** tab highlights critical integration or configuration errors that require immediate attention.
Use the **Filter by issue category** drop-down menu to sort the displayed errors. Categories include:
* **Setup:** Highlights foundational configuration errors (for example, Web Push failing in HTTP mode, missing service worker files, or unsupported Promises requiring polyfills).
* **Track:** Identifies data collection anomalies (for example, passing empty objects as attribute values, missing attribute strings, or blank notification titles).
* **Info:** Flags campaign-delivery errors (for example, attempting to track a card ID that has not been received or has been deleted).
Each issue card provides contextual information and a *Learn More* link for resolution guidance.
## Setup Info
Validate the foundational configuration and environment details of your MoEngage workspace directly from the browser. The **Setup Info** tab provides a comprehensive overview of your Web SDK implementation, structured into the following sections:
Displays core workspace metadata and configuration statuses, including:
* **Data center:** The region where your data is hosted.
* **App ID:** Your unique workspace identifier.
* **Multi ID enabled:** Indicates whether the Multi-ID tracking feature is active.
* **SPA mode enabled:** Indicates whether Single Page Application tracking is enabled.
* **Shopify:** Indicates whether moengage is integrated in the shopify website or not.
* **Billing type:** Outlines your active subscription model ( *Subscriber based billing*).
* **Is sub domain shared?:** Indicates whether the configuration supports shared sub-domains.
* **Settings last fetched:** Shows the relative timestamp of the most recent SDK configuration sync (for example, *Just now*).
Outlines the registration and operational status of your service worker, which is critical for Web Push notification delivery:
* **Status:** Indicates the current registration state (for example, *Registered*).
* **Service worker file:** The defined file path where the service worker script is hosted.
* **Scope:** Defines the URL domain scope that the service worker controls.
Details the specific configuration and current user permission state for Web Push prompts:
* **Opt-in type:** The configured prompt flow (for example, *2-step*).
* **Soft ask:** The user's interaction status with the custom browser prompt .
* **Hard ask:** The user's interaction status with the native browser permission prompt.
* **Push token:** Displays the active device push token if the user has successfully subscribed.
## Track
The **Track** tab monitors real-time data collection payloads and validates the event stream for the active session. It is divided into two sub-tabs:
* **Event**
* **User**
Monitors live behavioral and system events being tracked on the current page.
* **Search and Filter:** Use the dedicated search bar to quickly filter the live stream by a specific event name or attribute.
* **Status Indicators:** Successfully captured event payloads display a green **Tracked** badge next to the event name.
* **Payload Validation:** Select any tracked event to expand its payload and inspect the underlying key-value pairs. Depending on the specific event type (such as on-site messaging interactions or page views), you can validate detailed parameters, including:
* **Campaign Metadata:** Identifiers such as campaign\_id, campaign\_name, and cid.
* **Execution Properties:** Configuration details such as Variation ID, Locale Name, Locale ID, and targeted Campaign Tags.
* **User Context:** Session-specific booleans and strings evaluated at the time of the event, such as Logged In Status, First Visit, and the active URL.
**Note:** Ensure you expand the relevant events during testing to verify that custom attributes and campaign tracking identifiers are being passed correctly to the MoEngage backend.
Displays comprehensive session metadata, identity resolution details, and custom traits for the active user on the current webpage.
Manually clearing local storage resets the MoEngage SDK session and break event tracking. Always utilize the logoutUser() method before clearing localStorage.
* **User identity:** Indicates all the identification associated with the user within the MoEngage ecosystem.
* **Device connection:** Displays detailed information about the connected device and browser.
* **Device UID:** The unique cryptographic identifier generated for the current device or browser instance.
* **Device added:** Confirms whether the device has been successfully registered in the MoEngage backend (for example, *Yes*).
* **Session details:** Provides temporal and sequential data regarding the user's active browsing period.
* **Session ID:** The unique alphanumeric string representing the current active session.
* **Number of sessions:** The cumulative count of sessions associated with specific user on a pariticular device (for example, *93*).
* **Session started on:** The exact localized timestamp indicating when the current session initialized.
* **Session expires on:** The projected timestamp indicating when the current session will automatically terminate due to inactivity.
* **User attributes:** A collapsible list displaying the specific user attributes assigned to the user profile. Successfully logged attributes display a green **Tracked** badge alongside the attribute key and its corresponding masked or unmasked value.
## **Campaign**
The **Campaign** tab enables you to debug and inspect active campaigns targeted at the current user session. It is categorized by channel type:
* OSM (On-Site Messaging)
* Web Personalization
* Cards
**Campaign List:** Provides a searchable index of active campaigns detailing the Campaign Name, ID, and trigger count. **Campaign Properties:** Selecting a specific campaign reveals comprehensive execution details, including:
* Status, Template Type (for example, POP\_UP), Expiry Time, and Updated Time.
* Priority levels and behavioral configurations (for example, blocking, scrollable, sticky).
* The complete campaign payload, which can be copied directly to your clipboard for further analysis.
**Campaign Details**
Selecting a specific campaign card navigates to a detailed analysis view. This expanded view reveals comprehensive execution parameters and behavioral configurations, divided into the following sections:
* Select the **View in Dashboard** link within the Campaign details panel to open the specific campaign configuration directly within your MoEngage workspace.
* If a specific module or feature has not been integrated within your Web SDK implementation, MoEngage Assist displays a descriptive error state to assist with troubleshooting. For example, if the OSM module is missing, the extension indicates: *"OSM not integrated. To start seeing campaigns, please integrate the OSM module on your website."*
* **Campaign status:** Tracks current eligibility and delivery metrics.
* Validates Page eligibility and Display status.
* Displays the total page view impressions for the current load and the lifetime impressions.
* **Campaign properties:** Outlines the core structural configuration and rendering behavior defined in the campaign setup.
* **Metadata:** Includes Template type (for example, POP\_UP), Expiry time, Updated time, and Priority.
* **Rendering Rules:** Details specific UI/UX constraints such as Max show count, Auto dismiss after, Delay between same campaign, Blocking, Scrollable, and Sticky flags.
* **Trigger actions:** Details the specific behavioral conditions required to execute the campaign.
* Identifies the evaluated Primary event (for example, MOE\_PAGE\_EXIT) and confirms its trigger status.
* Displays the rule description and the configured Trigger delay (for example, *Immediately*).
* **Frequency capping:** Displays the global and page-level limits configured to prevent campaign fatigue.
* **Rules:** Outlines the maximum permitted displays for All campaigns (for example, *Max 3 Per day*) and specific Page load constraints.
* **Device frequency data:** Expand this section to view the specific device's execution counts against the configured rules. It breaks down consumption for the Last hour (LH) and the current Date.
# Standardizing Web SDK APIs for Cross-Platform Consistency
Source: https://moengage.com/docs/developer-guide/web-sdk/migration/standardizing-web-sdk-apis-for-cross-platform-consistency
Learn how to migrate MoEngage Web SDK APIs from snake_case to camelCase. Ensure cross-platform consistency and avoid deprecation warnings with this transition guide.
The SDK internal logic currently maps legacy names to new names to ensure backward compatibility. MoEngage recommends updating to the new syntax to maintain compatibility with future releases.
## Global API mapping
Use the standardized `camelCase` naming conventions for all new implementations.
### InitData Configurations
Old snake-cased `InitData` properties are now deprecated. Warning messages will be logged in the console if they are used.
| Legacy Property (Deprecated) | Standardized Property (New) |
| :--------------------------- | :-------------------------- |
| `project_id` | `projectId` |
| `app_id` | `appId` |
| `bots_list` | `botsList` |
| `disable_onsite` | `disableOnsite` |
| `disable_web_push` | `disableWebPush` |
### API Methods
Update your integration script and internal calls to use the following standardized methods:
| Legacy Method (Deprecated) | Standardized Method (New) |
| :------------------------- | :------------------------ |
| `track_event` | `trackEvent` |
| `track_page_view` | `trackPageView` |
| `handle_page_change` | `handlePageChange` |
| `call_web_push` | `callWebPush` |
| `on_cards_loaded` | `onCardsLoaded` |
| `add_user_attribute` | `setUserAttribute()` |
| `add_first_name` | `setFirstName()` |
| `add_last_name` | `setLastName()` |
| `add_email` | `setEmailId()` |
| `add_mobile` | `setMobileNumber()` |
| `add_user_name` | `setUserName()` |
| `add_gender` | `setGender()` |
| `add_birthday` | `setBirthDate()` |
| `update_unique_user_id` | `updateUniqueUserId` |
| `destroy_session` | `logoutUser` |
| `moe_events` | `moeEvents` |
Note: Existing camel-cased or single-word methods like `track`, `identifyUser`, and `getUserIdentities` remain unchanged.
## Impacted platforms
This transition affects all integrations that utilize the MoEngage Web SDK, including the following platforms and package managers:
* Google Tag Manager (GTM)
* Shopify
* VTEX
* NPM/CDN
* Flutter Web
## Integration scenarios
The migration path depends on your MoEngage SDK integration type. Identify your scenario below to determine the necessary actions.
### Fixed version
**Critical:** New `camelCase` methods are not available in legacy SDK versions. Do not refactor code until you update the SDK version in the source code to **2.71.00 and above**.
### New integrations
Implement the SDK using `camelCase` methods. This ensures the integration is natively compatible with modern JavaScript standards and prevents console warnings.
### CDN/NPM
If the script tag pulls the latest version, the SDK automatically maps legacy calls to the new logic.
**Recommendation:** To maintain a clean development environment and avoid deprecation warnings in the browser console, refactor these calls to `camelCase`.
## Troubleshooting
To verify your SDK methods or identify any lingering legacy code, you can enable debug logging to view deprecation warnings directly in your browser's console.
1. **Enable SDK logging:** You must first call the following method to enable verbose logs for the MoEngage SDK. This must be executed before you can see the logs:
```javascript theme={null}
Moengage.setDebugLevel(2)
```
2. Check the console logs: Open your browser's developer tools and navigate to the Console tab. If any deprecated methods are still in use, the SDK outputs an error or warning message.
Example: *\["destroy\_session" is deprecated. Please use "logoutUser" instead.]*
# Configure and Integrate On-site Messaging
Source: https://moengage.com/docs/developer-guide/web-sdk/onsite-messaging/configure-and-integrate-on-site-messaging
Configure on-site messaging campaigns to show personalized pop-ups and banners on your website.
On-site Messaging Campaigns allow you to show personalized pop-ups and non-intrusive banners on your website.
Web SDK integration for On-site Messaging will automatically start working on all the pages where the web SDK is integrated.
For more information, refer to [Web SDK Integration](/docs/developer-guide/web-sdk/web-sdk-integration/basic-integration/web-sdk-integration).
## Disabling On-site Messaging
Optional step for On-Site messaging
If you want to disable On-site Messaging on a few pages or all pages of your website, you need to add `disable_onsite: true` on the pages wherever MoEngage Web SDK is active as described:
```javascript JavaScript lines wrap theme={null}
Moengage = moe({
appId: "YOUR_WORKSPACE_ID",
env: 'LIVE',
logLevel: 0,
disable_onsite: true
});
```
## Callback to On-site messaging events
```javascript JavaScript lines wrap theme={null}
window.addEventListener('MOE_AUTOMATED_EVENTS', function (event) {
if (event.detail.name === 'MOE_ONSITE_MESSAGE_SHOWN' && event.detail.data && event.detail.data.length) {
//do some processing on event.detail.data
}
});
```
In the above example, we have `event.detail.name` = `'MOE_ONSITE_MESSAGE_SHOWN'`. Similarly, we provide callbacks to other On-site messaging events
| event.detail.name | description |
| ----------------------------------- | ---------------------------------------------------------------------------------------------------------- |
| MOE\_ONSITE\_MESSAGE\_SHOWN | On-site message is shown to the user |
| MOE\_ONSITE\_MESSAGE\_CLICKED | User clicks on the element on which `moe-inapp-click` class is present |
| MOE\_ONSITE\_MESSAGE\_DISMISSED | User clicks on the element on which `moe-inapp-close` class is present |
| MOE\_ONSITE\_MESSAGE\_AUTO\_DISMISS | The On-site message is auto-closed after the time configured while creating the On-site messaging campaign |
Here is a sample of `event.detail.data` when `event.detail.name` = `'MOE_ONSITE_MESSAGE_SHOWN'`
```javascript JSON lines wrap theme={null}
[
{
"key": "campaign_id",
"value": < the campaign ID >
},
{
"key": "campaign_name",
"value": < the campaign name >
},
{
"key": "type",
"value": "onsite" //this is fixed value
},
{
"key": "templateType",
"value": "POP_UP" //just an example, the templateType can be POP_UP, SELF_HANDLED or BANNER according to the type of template you had chosen for your campaign
},
{
"key": "cid",
"value": "643e2a69dcb2c753d3a1b8a3_F_T_ON_AB_1_P_0_L_0" //just an example, this is an extension of the campaign ID
},
{
"key": "moe_locale_name",
"value": "Default" //just an example
},
{
"key": "moe_locale_id",
"value": "0" //just an example
},
{
"key": "moe_variation_id",
"value": "1" //just an example
},
{
"key": "moe_logged_in_status",
"value": false //just an example, tells us if the user is a logged-in user
},
{
"key": "moe_first_visit",
"value": true //just an example
},
{
"key": "URL",
"value": < the url of the page on which the On-site message is shown >
}
]
```
`event.detail.data` of other On-site messaging events (eg: for `MOE_ONSITE_MESSAGE_DISMISSED`) will have similar (not identical) payload. You can check the data by logging it to the console: `console.log(event.detail.data)`
## Troubleshooting
### Sticky Banner type campaign is overlapping the website header.
Sticky banner type campaign has `fixed` position with `top: 0`. So if your website also has any fixed position element with `top: 0`, then it will overlap. This is the tech limitation and should be handled by your end.
Solution:
You can update the position of you fixed position element as soon as the OSM template is displayed from Moengage. Use this:
```javascript JavaScript lines wrap theme={null}
window.addEventListener('MOE_AUTOMATED_EVENTS', function (event) {
if (event.detail.name === 'MOE_ONSITE_MESSAGE_SHOWN' && event.detail.data && event.detail.data.length) {
const campaign = event.detail.data.find(item => item.key === "campaign_id");
if (campaign && campaign.value === < your campaign ID > ) {
//perform your action here
}
}
});
```
### On-site messaging campaign is not displayed.
Verify the following:
1. Check if the Trigger Action condition is matching or not
2. If you have Selected Pages for onsite messaging, then check whether the URL is matching or not
3. Check the segmentation of the Audience is matching or not.
4. Check the platform (Web or Mobile Web) is appropriately selected.
### On-site messaging test campaign is not working
Moengage Web SDK uses window\.opener.postMessage to communicate between the Moengage dashboard and your website, in order to show the test OSM campaign.
However, it may be that the window\.opener is blocked between different origin in your website. Please check whether `Cross-Origin-Opener-Policy': 'same-origin'` header is added in your website.
If the above header exist in your website, then the test campaign will not work. You can either remove this and test or create a live campaign and target yourself using segmentation.
For further assistance, please contact your MoEngage Customer Success Manager (CSM) or the Support team.
# Self Handled On-Site Messaging
Source: https://moengage.com/docs/developer-guide/web-sdk/onsite-messaging/self-handled-on-site-messaging
Receive on-site messaging campaign data as JSON and build custom UI for your website.
## Overview
Self-Handled On-site Messaging (OSM) is a powerful feature that gives developers the flexibility to receive campaign data directly as a JSON payload. Instead of having the MoEngage SDK render a predefined template, this approach allows your application to build and display a custom UI that seamlessly integrates with your website's native design and user experience.\
This method is ideal for creating highly customized, dynamic, and deeply integrated on-site messages.
You must use the MoEngage Web SDK version *2.55.0* or later.
## Receive Campaign Data
To receive campaign data, you must register a callback function. The MoEngage SDK executes this function when the trigger conditions for a Self-Handled OSM campaign are met.
### *Moengage.onsite.getSelfHandledOSM(callbackFunction)*
This method registers a listener for Self-Handled OSM campaigns. MoEngage recommends calling this method on page load to ensure the listener is active and ready to receive data.
Example Implementation:
```javascript JavaScript lines wrap theme={null}
Moengage.onsite.getSelfHandledOSM(function(fullCampaignData) {
// This callback function is the entry point for handling campaign data.
console.log("Received Self-Handled OSM Data:", fullCampaignData);
// Proceed with custom UI rendering and event tracking.
});
```
## Understand the Campaign Data Payload
When a campaign triggers your registered callback, the function receives a `fullCampaignData` object containing all the necessary information about the campaign.
Example `fullCampaignData` Payload:
```javascript JavaScript lines wrap theme={null}
[{
"campaignId": "000000000000000000000001",
"campaignName": "Holiday Sale Banner",
"expiryTime": "2025-12-31T23:59:59.000Z",
"updatedTime": "2025-10-06T10:30:00.000Z",
"status": "ACTIVE",
"jsonPayload": "{\"title\":\"Holiday Sale!\",\"message\":\"Get up to 50% off all items.\"}",
"dismissInterval": 86400,
"context": {
"campaign_name": "Holiday Sale Banner",
"cid": "000000000000000000000001_v2",
"moe_locale_id": "eng-us",
"moe_locale_name": "English (US)",
"moe_variation_id": "variation_b_12345"
},
"urlFilters": null
}]
```
### Payload Fields
| Field | Type | Description |
| --------------- | ------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| campaignId | String | The unique identifier for the campaign. |
| campaignName | String | The name of the campaign as defined in the MoEngage dashboard. |
| expiryTime | String | The expiry time of the campaign. |
| updatedTime | String | The time when the campaign was updated. |
| status | String | The campaign's current status. It will always be ACTIVE. |
| jsonPayload | String | A stringified JSON object containing the custom data for your UI. Parse this string to access its contents. |
| dismissInterval | Number | Specifies the time in seconds before the campaign can be automatically dismissed. The default value is 86400 (24 hours). |
| context | Object | An object containing metadata required for accurate campaign tracking. You must include this object when calling the tracking methods. |
| urlFilters | Object | Defines URL-based targeting rules for the campaign. Developers must implement logic to check whether the current page URL matches the defined targeting rules and display the campaign only when a match is found. If null, no URL-specific targeting applies. |
Example `urlFilters` Object
The `urlFilters` object defines the logic that determines on which pages a campaign can appear. The example below shows a configuration that displays the campaign when the URL contains either "loc" or "hos".
```javascript JavaScript lines wrap theme={null}
"urlFilters": {
"included_filters": {
"filters": [
{
"executed": true,
"execution": {
"count": 1,
"type": "atleast"
},
"action_name": "MOE_PAGE_URL_EVENT",
"attributes": {
"filter_operator": "or",
"filters": [
{
"name": "URL",
"data_type": "string",
"operator": "containsInTheFollowing",
"value": [
"loc"
],
"value1": "",
"negate": false,
"case_sensitive": false,
"array_filter_type": "any_of"
},
{
"name": "URL",
"data_type": "string",
"operator": "containsInTheFollowing",
"value": [
"hos"
],
"value1": "",
"negate": false,
"case_sensitive": false,
"array_filter_type": "any_of"
}
]
},
"filter_type": "actions"
}
],
"filter_operator": "or"
}
}
```
## Campaign Delivery Logic and Best Practices
When an event is triggered, MoEngage evaluates all eligible campaigns and selects a single campaign to deliver.
### Campaign Selection Algorithm:
* **Filtering**: MoEngage filters campaigns based on user eligibility and targeting criteria.
* **Exclusion**: MoEngage removes ineligible campaigns based on rules like frequency capping, delays, and expiration status.
* **Prioritization**: MoEngage sorts the remaining campaigns by priority (P0 is the highest).
* **Tie-Breaking**: If multiple campaigns share the highest priority, MoEngage selects the one with the most recent "last updated" timestamp.
Your `getSelfHandledOSM` callback receives only the single winning campaign.
### Best Practices for Campaign Organization:
To ensure you show the correct campaign in the right context, organize your campaigns with clear priorities.
* **Homepage Context**: Campaign A (P0), Campaign B (P1)
* **Product Page Context**: Campaign C (P0), Campaign D (P1)
* **Checkout Context**: Campaign E (P0)
## Track Campaign Statistics
Because your application controls the UI, you are responsible for notifying the SDK of user interactions. This step is essential for accurate analytics.
| Method | Description |
| ------------------------------------------ | ----------------------------------------------------------------------- |
| Moengage.onsite.selfHandledShown(data) | Call this method after the message is rendered and visible to the user. |
| Moengage.onsite.selfHandledClicked(data) | Call this method when the user clicks on the message. |
| Moengage.onsite.selfHandledDismissed(data) | Call this method when the user dismisses the message. |
### Construct the Tracking Data Object
The *data* object passed to the tracking methods is not the entire `fullCampaignData` object. You must construct a new, specific object for tracking.
Required `trackingData` Format:
```javascript JavaScript lines wrap theme={null}
const trackingData = {
campaignId: fullCampaignData.campaignId,
campaignName: fullCampaignData.campaignName,
context: fullCampaignData.context // Pass the context object exactly as received.
};
```
## Code Implementation Example
This example demonstrates the core logic for receiving data and preparing it for the tracking methods.
```javascript JavaScript lines wrap theme={null}
// Register the callback function to listen for campaigns.
Moengage.onsite.getSelfHandledOSM(function(fullCampaignData) {
console.log("Full campaign data received:", fullCampaignData);
var firstCampaignData = fullCampaignData[0];
// Construct the specific object required for tracking events.
const trackingData = {
campaignId: firstCampaignData.campaignId,
campaignName: firstCampaignData.campaignName,
context: firstCampaignData.context
};
// --- Example Tracking Calls ---
// Call this once your custom UI is rendered and visible.
Moengage.onsite.selfHandledShown(trackingData);
// In your button's click handler, call this.
// myButton.addEventListener('click', () => {
// Moengage.onsite.selfHandledClicked(trackingData);
// });
// In your dismiss element's click handler, call this.
// myDismissButton.addEventListener('click', () => {
// Moengage.onsite.selfHandledDismissed(trackingData);
// });
});
```
## Troubleshooting
If your `Moengage.onsite.getSelfHandledOSM` callback function is not being triggered, we recommend checking the following common issues:
* **SDK Initialization**: Ensure you call `getSelfHandledOSM()` only after the MoEngage SDK is successfully initialized. Calling this method too early in the page load sequence can prevent the callback from registering correctly.
* **Correct Method**: Verify you are using the `Moengage.onsite.getSelfHandledOSM()`method. Ensure you are not accidentally calling a method from a different module.
* **SDK Version Compatibility**: Check that your Web SDK version meets the minimum requirement specified in the Prerequisites.
* **Browser Console Errors**: Open the developer console in your browser and check for any JavaScript errors originating from the MoEngage SDK that could disrupt its operation.
# Configure and Integrate AMP Event Analytics
Source: https://moengage.com/docs/developer-guide/web-sdk/other-supported-web-sdk-integration/configure-and-integrate-amp-event-analytics
Track user attributes and events on your AMP pages using the MoEngage AMP analytics plugin.
MoEngage AMP event analytics plugin helps in track user attributes and events, and run third-party javascript. MoEngage AMP event analytics plugin ensures to address the differences in AMP pages and HTML page restriction for tracking user attributes and events.
The MoEngage AMP event analytics plugin is different from the AMP analytics module.
To add event tracking and user attribute tracking to your AMP pages follow these steps-
## Add AMP Analytics Script
Ensure to include the script in all of your AMP pages in the `` section of your .amp file where you want to use AMP analytics and track user attributes and events.
```HTML HTML lines wrap theme={null}
```
### Add Anywhere in your HTML
```HTML HTML lines wrap theme={null}
```
Note
Ensure to replace Your\_Workspace\_ID with the actual Workspace Id from MoEngage Dashboard -> Settings -> App -> General Settings
For dataCenter, please contact our support team to know more.
Note
To redirect data to test environment, append '\_DEBUG' to appId. For example, if your appId is \
YOUR\_WORKSPACE\_ID then for test environment, it would be YOUR\_WORKSPACE\_ID\_DEBUG
## Tracking Users
All the users visiting your AMP pages will be tracked automatically once you followed the above steps.\
But these users will be anonymous users by default.\
However, if any user of your website who visited your normal HTML pages earlier and has not deleted their cookies, will be treated as the same user in AMP pages also.
## Tracking Events
Page Viewed event is tracked by default if you followed the above steps.\
However with AMP framework limitations on event tracking, only a few kinds of events can be tracked such as `Page Viewed`, `Element Clicked`, `Page Scroll`.
For more information on the list of events, refer to [AMP Analytics Examples](https://amp.dev/documentation/examples/components/amp-analytics/).
### Example 1: Element Click Event
You can track a Click Event when an HTML element with id `test` is clicked as described:
```HTML HTML lines wrap theme={null}
```
| Parameter | Description |
| ---------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `on` | Type of event |
| `selector` | standard CSS selector |
| `request` | Should always be an event |
| `a` | Event attributes. In this example, the `title` is an attribute. Verify the change in the variable `title` using `data-vars-title` from the button element. |
| `e` | Event name |
### Example 2: Page Scroll Event
```HTML HTML lines wrap theme={null}
```
The scroll event needs `scrollSpec` object, that contains `verticalBoundaries` and `horizontalBoundaries`. At least one of the two properties is required for a scroll event to fire. The values for both of the properties should be arrays of numbers containing the boundaries on which a scroll event is generated. For instance, in the following code snippet, the scroll event will be fired when the page is scrolled vertically by 25%, 50% and 90%. The attributes sent here is `scrolledUpto` which holds an inbuilt variable `scrollTop` that provides the number of pixels that the user has scrolled from the top.
For more information about the list of all supported variables, refer to [Supported Variables](https://github.com/ampproject/amphtml/blob/main/docs/spec/amp-var-substitutions.md).
### Example 3: Form Submit Event
```HTML HTML lines wrap theme={null}
```
In the above example, we are setting the id of the form as the selector value (**#testForm**). Inside "extraUrlParams", "e" is the event name and "a" contains the key and the value which we want to track for this event.
## Tracking User Attributes
Important
If any user attribute is configured as an identity for your account, then track it as part of [login](#tracking-user-login-and-logout) instead of simply tracking it as a user attribute as it is shown in this section. Refer to [this document](https://www.moengage.com/docs/user-guide/data/user-data/unified-identity-identity-resolution) to learn more.
You can track user attributes using the "EVENT\_ACTION\_USER\_ATTRIBUTE" event. For example, you have this button on your website:
```HTML HTML lines theme={null}
```
Then, track the first name (considering it to be the user's first name) like this:
```HTML HTML lines wrap theme={null}
```
Similarly, to track the user's email upon clicking the below button
```HTML HTML lines theme={null}
```
```HTML HTML lines wrap theme={null}
```
In the same way, you can track other **pre-defined user attributes**
```HTML HTML lines wrap theme={null}
```
```HTML HTML lines wrap theme={null}
```
Here, USER\_ATTRIBUTE\_NAME has to be replaced with one of the following
```javascript JavaScript lines wrap theme={null}
USER_ATTRIBUTE_USER_EMAIL //for user email - value needs to be string, eg: "dom@level5.com"
USER_ATTRIBUTE_USER_NAME //for user name - value needs to be string, eg: "Dominick (Dom) Cobb"
USER_ATTRIBUTE_USER_FIRST_NAME //for user first name - value needs to be string, eg: "Dominick"
USER_ATTRIBUTE_USER_LAST_NAME //for user last name - value needs to be string, eg: "Cobb"
USER_ATTRIBUTE_USER_MOBILE //for user mobile - value needs to be string, eg: "+12399999999"
USER_ATTRIBUTE_USER_GENDER //for user gender - value needs to be string, eg: "M"
USER_ATTRIBUTE_USER_BDAY //for user birthday - value needs to be in date format, eg: new Date(1980, 2, 31)
```
To track a **custom user attribute**, in place of USER\_ATTRIBUTE\_NAME you have to use your own custom attribute name. For example-
```HTML HTML lines wrap theme={null}
```
In the above example, we are tracking a custom attribute named "colors". This attribute's value is an array- `["blue","green","red"]`
## Tracking User Login and Logout
[Previous way of logging-in users](/docs/developer-guide/web-sdk/other-supported-web-sdk-integration/user-login-in-amp-older-process) has been changed. The below method follows the User Identity Resolution feature of MoEngage. [Learn More](https://www.moengage.com/docs/user-guide/data/user-data/unified-identity-identity-resolution).
Ensure log in and log out of users are implemented correctly during the visit to your website and users are authenticated.
Important
If the user log in and log out is not handled correctly, user data may get corrupted. Refer [this section](https://www.moengage.com/docs/developer-guide/web-sdk/data-tracking/web-sdk-user-attributes-tracking#User-Login-and-Logout) for more details.
### Track Login
```HTML HTML lines wrap theme={null}
```
Here, we want to track ID "someUniqueId" and Email "[emailValue@emailDomain.com](mailto:emailValue@emailDomain.com)" as the identities for the user.
Important
If you are setting the ID, make sure to add/update its value in both `identifiers` -> `moe_user_id` as well as inside `identifiers` -> `user_identities`
```html HTML lines wrap theme={null}
```
Here, `uid` is ID and `u_em` is Email (Standard) attributes. Please refer to the below table for key names which need to be used to set standard user attributes as identities.
| User Attribute Name | Key name to be used in user\_identities |
| ------------------------ | --------------------------------------- |
| ID | uid |
| Email (Standard) | u\_em |
| Gender | u\_gd |
| Birthday | u\_bd |
| Name | u\_n |
| First Name | u\_fn |
| Last Name | u\_ln |
| Mobile Number (Standard) | u\_mb |
If we want to identify the user with just mobile number (for example)-
```HTML HTML lines wrap theme={null}
```
```html HTML lines wrap theme={null}
```
Note
Here, we do not have to send the value of mobile number in `identifiers` -> `moe_user_id` as Mobile and ID are two different attributes of a user. Only while adding/updating ID, the value of the ID has to be sent in `moe_user_id` as well as in `user_identities`.
Important
After tracking the login, all further attributes and events tracking should have the "identifiers" object defined inside the tracking code (with the "moe\_user\_id" and/or "user\_identities") as above, until logout event is performed. Otherwise, that attribute or event tracking will not be associated with this logged-in user in your MoEngage dashboard.
For example, if we want to track an event after identities have been set for the user-
```html HTML wrap theme={null}
```
```html HTML lines wrap theme={null}
```
### Track Identities Update
When updating the value of an identity, send the changed identities in `identifiers` -> `previous_identities`.
For example, let's assume you first set ID and Email as identities of the user.
```HTML HTML lines wrap theme={null}
```
```html HTML lines wrap theme={null}
```
Now, let's say you want to update the ID of the user.
```HTML HTML lines wrap theme={null}
```
```HTML HTML lines wrap theme={null}
```
Note
In the above example, we updated ID. You can update more than one identities but you have to mandatorily send all the changing identities in `previous_identities`.
### Track Logout
```HTML HTML lines wrap theme={null}
```
```html HTML lines wrap theme={null}
```
Important
After performing the above Moengage logout event, do NOT send "identifiers" with any further tracking code. Because the user had logged-out and further attribute/event tracking must not be associated with this user.
# Configure and Integrate AMP Web Push
Source: https://moengage.com/docs/developer-guide/web-sdk/other-supported-web-sdk-integration/configure-and-integrate-amp-web-push
Enable web push notifications on your AMP pages using the MoEngage AMP Web Push integration.
**AMP Web Push Integration Prerequisites**
1. Ensure that AMP analytics is integrated before integrating AMP Web Push.
2. Ensure that AMP analytics is integrated before integrating AMP Web Push.
3. MoEngage AMP Web Push Integration does not, by default, track the MoEngage default user attributes and events.
## AMP Web Push
Web Push Notification does not work by using the MoEngage Web SDK and serviceworker.js.\
Google has recently published a separate plugin to support the integration that enables News Publishers, Bloggers, or anyone who uses AMP to deliver their webpages quickly by encouraging users to subscribe using Push Notification.
Only Android devices are supported.
Follow these steps to integrate AMP Web Push:
## Add AMP Web Push script
Add this line in the `` section of your .amp file where you want to use Web Push.
```HTML HTML lines wrap theme={null}
```
## Add Helper files
Download the files corresponding to the dashboard you are using and ensure the files are available in the root directory of your website. Right-click and click **Save Link as...** to save the files:
| Dashboard host | Files |
| ------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| dashboard-01.moengage.com | 1. [amp-helper-frame.html](https://cdn.moengage.com/release/dc_1/amp-helper-frame.html) 2. [amp-permission-dialog.html](https://cdn.moengage.com/release/dc_1/amp-permission-dialog.html) 3. [serviceworker\_amp.js](https://cdn.moengage.com/release/dc_1/serviceworker_amp.js) |
| dashboard-02.moengage.com | 1. [amp-helper-frame.html](https://cdn.moengage.com/release/dc_2/amp-helper-frame.html) 2. [amp-permission-dialog.html](https://cdn.moengage.com/release/dc_3/amp-permission-dialog.html) 3. [serviceworker\_amp.js](https://cdn.moengage.com/release/dc_3/serviceworker_amp.js) |
| dashboard-03.moengage.com | 1. [amp-helper-frame.html](https://cdn.moengage.com/release/dc_3/amp-helper-frame.html) 2. [amp-permission-dialog.html](https://cdn.moengage.com/release/dc_3/amp-permission-dialog.html) 3. [serviceworker\_amp.js](https://cdn.moengage.com/release/dc_3/serviceworker_amp.js) |
| dashboard-04.moengage.com | 1. [amp-helper-frame.html](https://cdn.moengage.com/release/dc_4/amp-helper-frame.html) 2. [amp-permission-dialog.html](https://cdn.moengage.com/release/dc_4/amp-permission-dialog.html) 3. [serviceworker\_amp.js](https://cdn.moengage.com/release/dc_4/serviceworker_amp.js) |
## Add Code
Add the following code inside `` tag:
```HTML HTML lines wrap theme={null}
```
Replace the following:
* DOMAIN.COM with your actual domain.
* WORKSPACE\_ID with your Workspace ID available at MoEngage Dashboard > Settings > General.
Ensure all files are available at the same source path and suffixed with HTTPS.
## Add the Subscribe or Unsubscribe Buttons
Subscribe / Unsubscribe buttons or Amp Web Push Widgets are needed to subscribe or unsubscribe the user from AMP Web Push Notifications.
The following code adds the subscribe or unsubscribe buttons:
```HTML HTML lines wrap theme={null}
{/* A subscription widget */}
{/* An unsubscription widget */}