> For the complete documentation index, see [llms.txt](https://docs.digia.tech/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.digia.tech/digia-engage/developer-guides/moengage.md).

# MoEngage Integration

***

The Digia MoEngage plugin intercepts **MoEngage Self-Handled In-App** campaign payloads and pipes them directly into Digia's rendering engine. Each MoEngage campaign carries a single **`digia_campaign_key`** that maps to a campaign you design in the Digia Engage Dashboard. When the campaign triggers, the plugin resolves that key against the campaigns Digia pre-fetched at startup and instantly renders a 100% native experience on the device—no WebViews required. Supported experience types include bottom sheets, dialogs, inline banners, surveys, **tooltips**, and **spotlights**.

This guide walks you through the end-to-end integration process—covering SDK installation, plugin initialization, and how to map your dashboard campaigns to Digia's native components.

## 1. Prerequisites

Before integrating, ensure you have:

* **Digia Access Key**: Log in to the [Digia Engage Dashboard](https://engage.digia.tech), open your project, and copy your key from **Settings → App Settings**.
* **Active Dashboard Account**: Ensure you have an active MoEngage workspace with access to your App/Account IDs.
* **MoEngage SDK**: Installed and initialized (see [Flutter](https://developers.moengage.com/hc/en-us/articles/20661421128084-Getting-Started-with-Flutter-SDK), [Android](https://developers.moengage.com/hc/en-us/categories/360006147431-Android-SDK), [iOS](https://developers.moengage.com/hc/en-us/articles/4403900743828-SDK-Integration), or [React Native](https://developers.moengage.com/hc/en-us/articles/22105190881044-Getting-Started-with-React-Native-SDK#h_01HEJAHP5W49AASNSHF5614AHP) Quick Start).

### Minimum Supported Versions

{% tabs %}
{% tab title="React Native" %}

* **Environment:** React Native `0.83+`, React `19.2+`
* **Digia packages:** [`@digia-engage/core`](https://www.npmjs.com/package/@digia-engage/core), [`@digia-engage/moengage`](https://www.npmjs.com/package/@digia-engage/moengage)
* **MoEngage SDK:** [`react-native-moengage`](https://www.npmjs.com/package/react-native-moengage)
  {% endtab %}

{% tab title="Flutter" %}

* **Environment:** Dart `3.3+`, Flutter `3.35+`
* **Digia packages:** [`digia_engage`](https://pub.dev/packages/digia_engage), [`digia_moengage_plugin`](https://pub.dev/packages/digia_moengage_plugin)
* **MoEngage SDK:** [`moengage_flutter`](https://pub.dev/packages/moengage_flutter)
  {% endtab %}

{% tab title="Android" %}

* **Environment:** `minSdk 24`
* **Digia packages:** [`tech.digia:engage`](https://central.sonatype.com/artifact/tech.digia/engage), [`tech.digia:engage-moengage`](https://central.sonatype.com/artifact/tech.digia/engage-moengage)
* **MoEngage SDK:** [`com.moengage:moe-android-sdk`](https://central.sonatype.com/artifact/com.moengage/moe-android-sdk)
  {% endtab %}

{% tab title="iOS (Swift)" %}

* **Environment:** iOS `16+`
* **Digia packages:** [`DigiaEngage`](https://swiftpackageindex.com/Digia-Technology-Private-Limited/digia_engage_iOS), [`DigiaMoEngage`](https://swiftpackageindex.com/Digia-Technology-Private-Limited/digia_moengage_iOS)
* **MoEngage SDK:** [MoEngage iOS SDK](https://developers.moengage.com/hc/en-us/categories/360006471532-iOS-SDK)
  {% endtab %}
  {% endtabs %}

> Package versions aren't pinned in this guide — install the latest from the linked registry pages (npm, pub.dev, Maven Central, Swift Package Index).

***

## 2. Install

> **Note:** We assume the core MoEngage SDK is already installed and initialized as part of your standard app setup (see [Prerequisites](#1-prerequisites)). The snippets below only cover adding the Digia plugin packages.

{% tabs %}
{% tab title="React Native" %}
Install the Digia packages:

```bash
npm install @digia-engage/core @digia-engage/moengage
```

{% endtab %}

{% tab title="Flutter" %}
Add the Digia packages using the `flutter pub add` command:

```bash
flutter pub add digia_engage digia_moengage_plugin
```

{% endtab %}

{% tab title="Android" %}
Add dependencies in app `build.gradle.kts` (use the latest versions from [Maven Central](https://central.sonatype.com/artifact/tech.digia/engage)):

```kotlin
dependencies {
  implementation("tech.digia:engage:<latest>")
  implementation("tech.digia:engage-moengage:<latest>")
}
```

Sync the project (Gradle sync).
{% endtab %}

{% tab title="iOS (Swift)" %}
Add packages using **Xcode → File → Add Package Dependencies** and choose **Up to Next Major Version** — or in `Package.swift`, use the latest versions from [Swift Package Index](https://swiftpackageindex.com/Digia-Technology-Private-Limited/digia_engage_iOS):

```swift
dependencies: [
    .package(url: "https://github.com/Digia-Technology-Private-Limited/digia_engage_iOS.git", from: "<latest>"),
    .package(url: "https://github.com/Digia-Technology-Private-Limited/digia_moengage_iOS.git", from: "<latest>"),
],
```

Then add these products to your app target dependencies:

* `DigiaEngage`
* `DigiaMoEngage`
  {% endtab %}
  {% endtabs %}

***

## 3. Initialize Digia with MoEngage

Initialize Digia **after** the MoEngage SDK and **before** plugin registration.

{% tabs %}
{% tab title="React Native" %}

```tsx
import { useEffect } from 'react';
import ReactMoE from 'react-native-moengage';
import { Digia } from '@digia-engage/core';
import { DigiaMoEngagePlugin } from '@digia-engage/moengage';

export function RootApp() {
  useEffect(() => {
    (async () => {
      // 1. MoEngage SDK — activate the JS bridge.
      ReactMoE.initialize('YOUR_MOENGAGE_WORKSPACE_ID');

      // 2. Initialize Digia before registering any CEP plugin.
      await Digia.initialize({
        apiKey: 'YOUR_ACCESS_KEY',
      });

      // 3. Register the Digia MoEngage plugin. Pass the ReactMoE module directly.
      Digia.register(new DigiaMoEngagePlugin({ moEngage: ReactMoE }));
    })().catch(console.error);
  }, []);

  return <AppNavigator />;
}
```

{% endtab %}

{% tab title="Flutter" %}

```dart
import 'package:digia_moengage_plugin/digia_moengage_plugin.dart';
import 'package:digia_engage/digia_engage.dart';
import 'package:moengage_flutter/moengage_flutter.dart';
import 'package:flutter/material.dart';

Future<void> main() async {
  WidgetsFlutterBinding.ensureInitialized();

  // 1. MoEngage SDK — initialise activates the native SDK already configured as a prerequisite.
  final moEngage = MoEngageFlutter('YOUR_MOENGAGE_APP_ID');
  moEngage.initialise();

  // 2. Initialize Digia before registering any CEP plugin.
  await Digia.initialize(
    DigiaConfig(apiKey: 'YOUR_ACCESS_KEY'),
  );

  // 3. Register the Digia MoEngage plugin.
  Digia.register(MoEngagePlugin(instance: moEngage));
  runApp(const MyApp());
}
```

{% endtab %}

{% tab title="Android" %}

```kotlin
import android.app.Application
import com.digia.engage.Digia
import com.digia.engage.DigiaConfig
import com.digia.engage.moengage.DigiaMoEngagePlugin

class MyApplication : Application() {
    override fun onCreate() {
        super.onCreate()

        // 1. MoEngage SDK — initialized as a prerequisite.
        // MoEngage.initialiseDefaultInstance(moEngage)

        // 2. Initialize Digia before registering any CEP plugin.
        Digia.initialize(
            context = applicationContext,
            config = DigiaConfig(apiKey = "YOUR_ACCESS_KEY"),
        )

        // 3. Register the Digia MoEngage plugin. Context is required to resolve the MoEngage instance.
        Digia.register(DigiaMoEngagePlugin(applicationContext))
    }
}
```

{% endtab %}

{% tab title="iOS (Swift)" %}

```swift
import SwiftUI
import DigiaEngage
import DigiaMoEngage

@main
struct MyApp: App {
    init() {
        // 1. MoEngage SDK — initialized as a prerequisite.
        // MoEngage.sharedInstance.initializeDefaultInstance(...)

        // 2. Initialize Digia before registering any CEP plugin.
        Task {
            do {
                try await Digia.initialize(DigiaConfig(apiKey: "YOUR_ACCESS_KEY"))

                // 3. Register the Digia MoEngage plugin.
                Digia.register(DigiaMoEngagePlugin())
            } catch { }
        }
    }

    var body: some Scene {
        WindowGroup {
            RootView()
        }
    }
}
```

{% endtab %}
{% endtabs %}

***

## 4. How Digia Maps to MoEngage Campaigns

> If you haven't read [How It Works](/digia-engage/start-here/readme.md#how-it-works) in the overview, start there. The section below covers only what is MoEngage-specific.

In short: MoEngage decides **who** sees a campaign and **when**. Digia decides **what** is rendered. The two are linked by a `digia_campaign_key` you set in the MoEngage dashboard — the Digia plugin reads that key, resolves it against its local cache, and renders the right experience natively.

The table below shows how each Digia experience type maps to a MoEngage campaign type and which SDK component handles it on the app side:

| Experience Type | Digia Component             | MoEngage Campaign Type | Description                                                                                                        |
| --------------- | --------------------------- | ---------------------- | ------------------------------------------------------------------------------------------------------------------ |
| **Nudge**       | `DigiaHost`                 | Self-Handled In-App    | Renders overlay experiences (bottom sheets, dialogs) above app content.                                            |
| **Guide**       | `DigiaAnchor` + `DigiaHost` | Self-Handled In-App    | Step-by-step tooltip/spotlight flows anchored to specific UI elements. Each step targets a registered `anchorKey`. |
| **Survey**      | `DigiaHost`                 | Self-Handled In-App    | Multi-step survey flows rendered as overlays.                                                                      |
| **Inline**      | `DigiaSlot`                 | Self-Handled In-App    | Renders inline content (banners, cards) within the app layout at a matching placement.                             |

Here is the end-to-end flow for a Self-Handled In-App campaign (Nudge, Guide, Survey, or Inline):

```mermaid
sequenceDiagram
    participant Dev as You
    participant Digia as Digia Dashboard
    participant MoE as MoEngage Dashboard
    participant App as App (Runtime)

    Dev->>Digia: Create campaign, copy Digia campaign key
    Dev->>MoE: Create Self-Handled In-App campaign<br/>Set digia_campaign_key = <Digia campaign key> in Key-Value Pairs

    Note over App: On app start
    App->>App: Digia.initialize() pre-fetches campaigns into local cache

    Note over App: User matches the MoEngage trigger
    MoE->>App: Fires Self-Handled In-App payload with digia_campaign_key
    App->>App: Digia plugin reads key, resolves from cache
    App-->>App: Renders native experience (no network round-trip)
```

### The Campaign Contract

Whatever the experience type, a Digia-powered MoEngage campaign carries the same payload in its **Campaign data / Key-Value Pairs** field — a single JSON object:

```json
{
  "digia_campaign_key": "promo_offer_sheet",
  "variables": {
    "coupon": "SAVE20",
    "offer_title": "Limited Offer"
  }
}
```

* **`digia_campaign_key`** (required) — the key of the Digia campaign to render. This alone determines the experience type (nudge, guide, survey, or inline) and its design; nothing about the UI lives in the MoEngage payload.
* **`variables`** (optional) — a JSON object of runtime values interpolated into the campaign at render time.

Every experience type — nudge, guide, survey, and inline — is delivered through the same **Self-Handled In-App** campaign with this same payload. The per-experience sections below show it in context.

***

## 5. Self-Handled In-App Campaigns

MoEngage supports a delivery mode called **Self-Handled In-App Campaigns** — instead of MoEngage rendering its own campaign UI, the SDK delivers the campaign payload to your app code. The Digia MoEngage plugin registers a self-handled in-app listener that intercepts this payload, extracts the **`digia_campaign_key`**, resolves it against the Digia campaigns pre-fetched at startup, and delegates rendering to Digia's runtime. **Without setting the campaign template type to Self Handled in the MoEngage dashboard, the payload will not be delivered to the plugin.**

No device-side sync is required — the plugin responds automatically once registered. Configure your campaign in the MoEngage dashboard:

1. Log in to the **MoEngage dashboard** and go to **Engage → In-App (NATIV)**.
2. Click **Create Campaign** and give it a name.
3. Set the **Template type** to **Self Handled**.
4. Configure the event trigger, audience, and schedule as required.
5. In the **Campaign data / Key-Value Pairs** field, add a key named **`digia_campaign_key`** with the Digia campaign key copied from your Digia dashboard.

See [MoEngage Self-Handled In-App](https://help.moengage.com/hc/en-us/articles/360051330591-Self-Handled-In-App-Template#h_01HP6KGBAVZDEV75BEFKRZ2M7R).

***

## 6. Setting Up Nudges

### Add the Nudge Container

Wrap your app root so overlay campaigns can render above app content.

{% tabs %}
{% tab title="React Native" %}
Mount `<DigiaHost />` once at your app root.

```tsx
import React from 'react';
import { View } from 'react-native';
import { DigiaHost } from '@digia-engage/core';

export default function RootLayout() {
  return (
    <View style={{ flex: 1 }}>
      <DigiaHost />
      <AppNavigator />
    </View>
  );
}
```

{% endtab %}

{% tab title="Flutter" %}

```dart
import 'package:digia_engage/digia_engage.dart';
import 'package:flutter/material.dart';

MaterialApp(
  navigatorObservers: [DigiaNavigatorObserver()],
  builder: (context, child) => DigiaHost(child: child!),
  home: const HomeScreen(),
)
```

{% endtab %}

{% tab title="Android" %}
**Jetpack Compose**

```kotlin
import com.digia.engage.DigiaHost

DigiaHost {
    AppNavHost()
}
```

{% endtab %}

{% tab title="iOS (Swift)" %}

```swift
import SwiftUI
import DigiaEngage

struct RootContainerView: View {
    var body: some View {
        DigiaHost {
            AppRootView()
        }
    }
}
```

{% endtab %}
{% endtabs %}

### Trigger a Nudge from MoEngage

Create a Self-Handled In-App campaign in MoEngage. In the **Campaign data / Key-Value Pairs** field, provide the `digia_campaign_key` that links to your Digia campaign:

```json
{
  "digia_campaign_key": "promo_offer_sheet"
}
```

| Field                | Required | Example value         | Description                                                                                                                                      |
| -------------------- | -------- | --------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------ |
| `digia_campaign_key` | Yes      | `promo_offer_sheet`   | The Digia campaign key copied from your Digia dashboard. Its type (nudge / guide / survey / inline) and design come from Digia, not the payload. |
| `variables`          | No       | `{"coupon":"SAVE20"}` | Optional JSON map of runtime variables interpolated into the campaign at render time.                                                            |

> **Linking:** In the Digia dashboard, copy the **Digia campaign key** assigned to your campaign. In the MoEngage Self-Handled In-App campaign's Key-Value Pairs, add a key named **`digia_campaign_key`** with that value. If the values differ, the plugin cannot resolve the campaign and nothing renders.

> **`variables` format:** Enter the value as plain JSON text directly in the field — e.g., `{"coupon":"SAVE20"}`. Do not wrap it in outer quotes. The plugin JSON-decodes the payload automatically.

***

## 7. Setting Up Inline Widgets

`DigiaSlot` is a composable/widget that renders Digia-powered content inline within your screen layout. Each slot is identified by a `placementKey`. When a Self-Handled In-App campaign whose `digia_campaign_key` maps to an **inline** Digia campaign triggers, Digia renders that campaign into the slot whose `placementKey` matches the slot key configured on the campaign in the Digia dashboard. If no campaign is active for a slot, it collapses to zero height.

### Add an Inline Slot

Place `DigiaSlot` where you want inline content rendered.

{% tabs %}
{% tab title="React Native" %}

```tsx
import React from 'react';
import { ScrollView } from 'react-native';
import { DigiaSlotView } from '@digia-engage/core';

export function HomeScreen() {
  return (
    <ScrollView>
      {/* Auto-sizes to match native content height. Pass style={{ height: N }} to pin a fixed height. */}
      <DigiaSlotView placementKey="home_hero_banner" />

      <ProductCarousel />

      <DigiaSlotView placementKey="product_offers" />

      <RecommendationList />
    </ScrollView>
  );
}
```

{% endtab %}

{% tab title="Flutter" %}

```dart
import 'package:digia_engage/digia_engage.dart';
import 'package:flutter/material.dart';

class HomeScreen extends StatelessWidget {
  const HomeScreen({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Home')),
      body: ListView(
        children: const [
          DigiaSlot('home_hero_banner'),
          SizedBox(height: 16),
          ProductCarousel(),
          SizedBox(height: 16),
          DigiaSlot('product_offers'),
          SizedBox(height: 16),
          RecommendationList(),
        ],
      ),
    );
  }
}
```

{% endtab %}

{% tab title="Android" %}
**Jetpack Compose**

```kotlin
import com.digia.engage.DigiaSlot

Column {
    DigiaSlot(placementKey = "home_hero_banner")
    DigiaSlot(placementKey = "product_offers")
}
```

**XML Layout**

For View/XML layouts, use `DigiaSlotView`. It wraps the Compose `DigiaSlot` API and reads `app:placementKey` from XML.

```xml
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="vertical">

    <com.digia.engage.DigiaSlotView
        android:id="@+id/homeHeroBanner"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        app:placementKey="home_hero_banner" />

    <com.digia.engage.DigiaSlotView
        android:id="@+id/productOffers"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        app:placementKey="product_offers" />

</LinearLayout>
```

Or set the placement programmatically after inflation:

```kotlin
import com.digia.engage.DigiaSlotView

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_home)

    findViewById<DigiaSlotView>(R.id.homeHeroBanner).placementKey = "home_hero_banner"
    findViewById<DigiaSlotView>(R.id.productOffers).placementKey = "product_offers"
}
```

`DigiaSlotView` requires `DigiaHost` to be mounted in the same window so the Digia rendering engine is already mounted before the slot renders.
{% endtab %}

{% tab title="iOS (Swift)" %}

```swift
import SwiftUI
import DigiaEngage

struct HomeScreen: View {
    var body: some View {
        ScrollView {
            VStack(spacing: 16) {
                DigiaSlot(placementKey: "home_hero_banner")
                ProductCarousel()
                DigiaSlot(placementKey: "product_offers")
                RecommendationList()
            }
            .padding()
        }
    }
}
```

{% endtab %}
{% endtabs %}

### Serve Inline Content from MoEngage

Inline campaigns are delivered the same way as nudges — through a Self-Handled In-App campaign carrying a `digia_campaign_key`. Build the inline campaign in the Digia dashboard (it defines which slot / `placementKey` it targets), then set `digia_campaign_key` to the Digia campaign key from that campaign:

```json
{
  "digia_campaign_key": "home_hero_banner_campaign"
}
```

| Field                | Required | Description                                                                                                                             |
| -------------------- | -------- | --------------------------------------------------------------------------------------------------------------------------------------- |
| `digia_campaign_key` | Yes      | The Digia campaign key of an **inline** campaign copied from your Digia dashboard. The campaign defines which slot key it renders into. |

The app-side `DigiaSlot` / `DigiaSlotView` `placementKey` must match the slot key configured on that campaign in the Digia dashboard.

{% hint style="info" %}
Inline content stays loaded once a campaign renders it — it doesn't clear when the user's context changes. To clear it (for example on logout), see [Managing Inline Content](/digia-engage/developer-guides/inline-content.md).
{% endhint %}

***

## 8. Setting Up Guides

`DigiaAnchor` registers a UI element as a named anchor for tooltip and spotlight Guide campaigns. When a Guide campaign runs, the SDK looks up the `anchorKey` to position the tooltip bubble or spotlight cutout relative to that element.

Wrap any element you want to anchor and give it a unique `anchorKey`. The key must match exactly what is configured for that step in the Digia dashboard Guide campaign.

{% tabs %}
{% tab title="React Native" %}

```tsx
import { DigiaAnchorView } from '@digia-engage/core';

// Anywhere in your screen component:
<DigiaAnchorView anchorKey="buy_now_button">
  <BuyNowButton />
</DigiaAnchorView>
```

{% endtab %}

{% tab title="Flutter" %}

```dart
import 'package:digia_engage/digia_engage.dart';

// Anywhere in your widget tree:
DigiaAnchor(
  anchorKey: 'buy_now_button',
  child: BuyNowButton(),
)
```

{% endtab %}

{% tab title="Android" %}
**Jetpack Compose**

```kotlin
import com.digia.engage.DigiaAnchor

// Anywhere in your composable:
DigiaAnchor(anchorKey = "buy_now_button") {
    BuyNowButton()
}
```

**XML Layout**

```xml
<com.digia.engage.DigiaAnchorView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    app:anchorKey="buy_now_button">

    <!-- Your view -->
    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Buy Now" />

</com.digia.engage.DigiaAnchorView>
```

{% endtab %}

{% tab title="iOS (Swift)" %}

```swift
import DigiaEngage

// Anywhere in your view hierarchy:
DigiaAnchor(anchorKey: "buy_now_button") {
    BuyNowButton()
}
```

{% endtab %}
{% endtabs %}

> **Note:** If the anchor is not mounted when a Guide campaign fires, the SDK logs an `anchor_not_on_screen` health event and skips that step. Ensure the anchor is on screen before navigating to the Guide step.

### Link the MoEngage Campaign to Digia

To connect a MoEngage campaign to your Digia Guide, copy the **Digia campaign key** assigned to your Guide campaign in the Digia dashboard. Then, in the MoEngage Self-Handled In-App campaign's Key-Value Pairs, add a key named **`digia_campaign_key`** with that value:

```json
{
  "digia_campaign_key": "onboarding_tour"
}
```

> **Note:** The `digia_campaign_key` value must exactly match the Digia campaign key from the dashboard. If they differ, the plugin cannot resolve the campaign and nothing renders.

***

## 9. Setting Up Surveys

Surveys are multi-step questionnaire flows you design in the Digia dashboard and render as overlays in the nudge host — no extra app-side widget is required beyond the [nudge container](#add-the-nudge-container). A survey is just another campaign type: create it in Digia, then trigger it from MoEngage by referencing its Digia campaign key via `digia_campaign_key`, exactly like a nudge.

1. Build the survey campaign in the Digia Engage Dashboard and copy its Digia campaign key.
2. Ensure your app mounts the nudge container — `DigiaHost` at the app root (all platforms).
3. Create a MoEngage Self-Handled In-App campaign and add a `digia_campaign_key` Key-Value Pair set to the survey's Digia campaign key.

```json
{
  "digia_campaign_key": "nps_q3_survey"
}
```

The survey renders over your app content and reports completion back through the plugin's analytics events.

***

## 10. Test Your Integration

Follow these steps to verify end-to-end functionality before releasing:

1. **Verify initialization order** — `Digia.initialize` completes, then `Digia.register(...)` with the MoEngage plugin (`MoEngagePlugin` on Flutter, `DigiaMoEngagePlugin` on React Native), before `runApp` / first activity.
2. **Verify host placement** — `DigiaHost` must be in `MaterialApp.builder` (Flutter), the root Composable (Android Compose), or the root SwiftUI wrapper (iOS). For React Native, mount `<DigiaHost />` once at the app root.
3. **Set campaign to Self Handled** — ensure the MoEngage in-app campaign template type is **Self Handled** in the MoEngage dashboard (see [Self-Handled In-App Campaigns](#5-self-handled-in-app-campaigns)).
4. **Create a test campaign** — create a Self-Handled In-App campaign with a `digia_campaign_key` pointing to a known Digia campaign and trigger it immediately for your test device.
5. **Verify inline slots** — place `DigiaSlot` / `DigiaSlotView` on a visible screen, trigger a Self-Handled In-App campaign whose `digia_campaign_key` points to an inline Digia campaign targeting that slot, and confirm content renders.
6. **Verify guides** — register a `DigiaAnchor` / `DigiaAnchorView` with an `anchorKey`, point a Guide campaign step at it, and confirm the tooltip/spotlight anchors correctly.
7. **Verify screen tracking** — navigate between screens and confirm that screen-triggered campaigns fire on the correct screen (use `DigiaNavigatorObserver` on Flutter, `Digia.setCurrentScreen(...)` or `digiaScreen("...")` in Android View/XML apps, route/screen change hooks calling `Digia.setCurrentScreen(...)` on Swift, and navigation listeners calling `Digia.setCurrentScreen(...)` on React Native). See [Screen Targeting](/digia-engage/developer-guides/screen-targeting.md) to scope campaigns to specific screens.

***

## 11. Troubleshooting

### Campaign not rendering

* Confirm initialization order — `Digia.initialize(...)` then `Digia.register(...)` with the MoEngage plugin (`MoEngagePlugin` on Flutter, `DigiaMoEngagePlugin` on React Native).
* Ensure the MoEngage campaign template type is set to **Self Handled** in the MoEngage dashboard.
* **iOS (Swift)**: Confirm `MoEngage.sharedInstance.initializeDefaultInstance(...)` runs at app start and `Info.plist` includes `MOEnvType`.
* **React Native**: Confirm `Digia.initialize(...)` and `Digia.register(new DigiaMoEngagePlugin(...))` are called once during app startup.
* Ensure `DigiaHost` is mounted at the app root.
* **React Native**: Ensure `<DigiaHost />` is mounted once at app root, and confirm `Digia.initialize(...)` completes before `Digia.register(...)`.
* Check `digia_campaign_key` exactly matches the Digia campaign key (case-sensitive).
* Verify campaign eligibility and trigger conditions in the MoEngage dashboard.

### Inline content not showing

* Confirm the Self-Handled In-App payload includes a `digia_campaign_key` that maps to an **inline** Digia campaign.
* Ensure the campaign's slot key (configured in the Digia dashboard) matches `DigiaSlot('placement_key')` or `DigiaSlotView app:placementKey` exactly (case-sensitive).
* Verify the slot exists on the currently visible screen.
* **React Native**: `DigiaSlotView` auto-sizes to match native content height. If it still appears collapsed, confirm the inline campaign is active and the `placementKey` matches. To pin a fixed height, pass `style={{ height: 180 }}`.

### Guide (tooltip / spotlight) not anchoring

* Ensure the element is wrapped in `DigiaAnchor` / `DigiaAnchorView` with an `anchorKey` that exactly matches the step configured in the Digia dashboard Guide campaign.
* Confirm the anchor is mounted on screen when the campaign fires — if not, the SDK logs `anchor_not_on_screen` and skips the step.
* **React Native**: Confirm `<DigiaHost />` is mounted at the app root.

### Screen-triggered campaign not firing

* Add `DigiaNavigatorObserver()` to `navigatorObservers` (Flutter).
* Call `Digia.setCurrentScreen(...)` on navigation changes (Android Compose: `navController.addOnDestinationChangedListener`).
* **Android XML / Views**: Call `digiaScreen("screen_name")` from `Activity.onResume()` or `Fragment.onResume()`.
* For unnamed routes, call `Digia.setCurrentScreen('screen_name')` manually.
* **iOS (Swift)**: On tab/route changes, call `Digia.setCurrentScreen("screen_name")` from your navigation layer.
* **React Native**: Wire navigation change events (React Navigation / Expo Router) to `Digia.setCurrentScreen(routeName)`.

### MoEngage SDK not initialized (Android)

* Ensure `MoEngage.initialise(moEngage)` is called in `Application.onCreate()` before `Digia.register(...)`.
* Verify MoEngage credentials are correctly configured per [MoEngage Android Quick Start](https://developers.moengage.com/hc/en-us/categories/360006147431-Android-SDK).

***

## Official MoEngage References

* [Flutter SDK](https://developers.moengage.com/hc/en-us/articles/20661421128084-Getting-Started-with-Flutter-SDK)
* [Android Quick Start](https://developers.moengage.com/hc/en-us/categories/360006147431-Android-SDK)
* [iOS SDK Integration](https://developers.moengage.com/hc/en-us/categories/360006471532-iOS-SDK)
* [React Native Quick Start Guide](https://developers.moengage.com/hc/en-us/categories/4404199274900-React-Native-SDK)
