# 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": "

See the attached brochure.

", "attachments": [ { "file_type": "URL", "url": "https://example.com/brochure.pdf" } ] } } } } ``` ```json Personalized attachment theme={null} { "campaign_content": { "content": { "email": { "subject": "Your invoice", "from_address": "billing@example.com", "html_content": "

Your invoice is attached.

", "attachments": [ { "file_type": "PERSONALIZED_ATTACHMENT", "url": "https://example.com/invoices/{{UserAttribute['User ID']}}.pdf" } ] } } } } ```
## A/B test variations `campaign_content.variation_details` configures A/B testing. | Field | Type | Notes | | :------------------------------- | :-------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------- | | `distribution_type` | enum | `MANUAL` or `SHERPA`. | | `no_of_variations` | integer (≥ 1) | The number of variations. | | `manual_distribution_percentage` | object | A map of variation name to integer percentage. Values must be positive integers that sum to 100. **Required** when `distribution_type` is `MANUAL`. | | `sherpa_campaign_duration` | integer (hours) | The window over which Sherpa collects performance data before declaring a winning variation. **Required** when `distribution_type` is `SHERPA`. | | `sherpa_distribution_metric` | enum | `OPEN RATE`, `CLICK RATE`, or `BOTH`. **Required** when `distribution_type` is `SHERPA`. | When `variation_details` is set, `campaign_content.content` must use the locale-keyed and variation-keyed shape. See [Content payload structure](#content-payload-structure). ```json theme={null} { "campaign_content": { "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": "Variant A", "message": "Try us today" } } } }, "variation_2": { "push": { "android": { "template_type": "BASIC", "basic_details": { "title": "Variant B", "message": "Save 20% today" } } } } } } } } ``` ```json theme={null} { "campaign_content": { "variation_details": { "distribution_type": "SHERPA", "no_of_variations": 2, "sherpa_campaign_duration": 24, "sherpa_distribution_metric": "OPEN RATE" }, "content": { "default": { "variation_1": { "email": { "subject": "Variant A", "html_content": "

Try us today

" } }, "variation_2": { "email": { "subject": "Variant B", "html_content": "

Save 20% today

" } } } } } } ```
## 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 ``` monitor_your_usage.png | 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: Newuserprofile ## 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. The OAuth configuration ID appears as the last segment of the Authorization configuration page URL 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. Multi AP Ikey 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: alt text ### 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: alt text 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**. api-dashboard 3. Copy your **Workspace ID** and the relevant **API Key**. You can perform authentication using a client like Postman as follows: Authorization ### 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). Android Cards # 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} ``` 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. Emptystate 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. Latest Popupimage # 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. Latest Nudgeimage 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. HTMLGUYS 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**. FCM Step1 4. On the **Project Settings** page, navigate to the **Service accounts** tab. Firebase Admin SDK 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)**. Mceclip4 8. After the JSON is configured, enable FCM in the [cloud console](https://console.cloud.google.com/apis/library/fcm.googleapis.com). Enable FCM # 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. ![MavenBadge](https://img.shields.io/maven-central/v/com.moengage/inbox-core) ## 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. Mceclip0(1) ## 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** Appsecret ## 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** Appsecret1 ## 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. Packagename 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).   Smalliconpath 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). Fetchthekeys ### 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: Nextttt(1) ## 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. Maven Central ```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.