In-App Takeover setup
Integrate the EngageApp SDK (Web or Flutter) so full-screen in-app takeover campaigns render natively when your users open your app.
An In-App Takeover is a full-screen message rendered natively by our SDK when your end-user next opens your app. Delivery is pull-based: when a campaign targets a contact, the takeover is queued; the SDK fetches and renders it on the next app foreground, then reports impressions, clicks, and dismissals back to EngageApp automatically.
1. Activate the channel
Go to Channels → New → In-App Takeover. This is a platform-managed channel — there are no provider credentials. You can tune the display rules:
- Default lifetime (hours) — how long a takeover stays fetchable before it expires unshown.
- Max per app open — how many takeovers the SDK shows per foreground.
- Min gap between (seconds) — minimum spacing between takeovers.
2. Create a publishable key
Under Settings → API keys, choose Publishable (pk_) and click Create. Copy the pk_… value — it is shown only once. Publishable keys are safe to embed in your app and are sent by the SDK as the X-Publishable-Key header.
3. Author a takeover template
Go to Templates → New → In-App Takeover. Set a headline, body, optional image or video, theme colors, and call-to-action buttons. Each button has an action: deeplink (route within your app), url (open a web URL), or dismiss. Use {{ first_name }} and other variables — they are resolved per contact at send time. Then send it from a campaign's send message step using your In-App channel.
Full-screen video
Set the media block to Video and paste an MP4 (H.264) URL. Video defaults to Full screen: it fills the takeover edge-to-edge with your copy and buttons laid over it behind a readability scrim. Switch the placement to Banner at top to keep the classic hero layout instead — the same two placements work for images.
- Poster image — the still frame shown before playback starts, and the fallback when video can't play. Required if you turn autoplay off.
- Autoplay / Loop / Muted — all on by default. Browsers and mobile OSes block autoplay with sound, so an unmuted video waits for a tap; the Web SDK retries muted and then shows playback controls.
On Flutter, playback is opt-in so the SDK stays dependency-free: pass a videoBuilder to EngageInAppConfig (see step 5). Without one, video takeovers render the poster image.
4. Integrate the Web SDK
Install @engageapp/inapp-web:
npm install @engageapp/inapp-web
Initialise, register handlers, identify your user, and fetch on every app foreground:
import { EngageInApp } from '@engageapp/inapp-web';
// 1. Initialise with your PUBLISHABLE key (safe in client code).
EngageInApp.init({
publishableKey: 'pk_your_key'
// apiBaseUrl defaults to https://api.engageapp.xyz
});
// 2. Handle button taps (deep links / URLs) in your app.
EngageInApp.setHandlers({
onButtonTap: (action) => {
if (action.type === 'deeplink') router.go(action.value);
if (action.type === 'url') window.location.assign(action.value);
}
});
// 3. Identify the signed-in user by your own id.
await EngageInApp.identify('user-123', { firstName: 'Ada' });
// 4. Fetch + render the next pending takeover. Call on every
// app foreground / route into the app.
await EngageInApp.onAppForeground();
The SDK renders the takeover as a full-screen overlay and reports the impression, clicks, and dismissal for you. See the Web SDK reference for the full API.
5. Integrate the Flutter SDK
Add engageapp_inapp:
flutter pub add engageapp_inapp
Attach a navigator key so the SDK can present the takeover without a BuildContext, then initialise and identify:
final navigatorKey = GlobalKey<NavigatorState>();
void main() {
EngageInApp.instance.init(EngageInAppConfig(
publishableKey: 'pk_your_key',
navigatorKey: navigatorKey,
));
EngageInApp.instance.setHandlers(
onButtonTap: (action, _) {
if (action.type == 'deeplink') myRouter.go(action.value);
},
);
runApp(MyApp(navigatorKey: navigatorKey));
}
// Attach the same key to your MaterialApp:
// MaterialApp(navigatorKey: navigatorKey, home: HomeScreen());
// After sign-in:
await EngageInApp.instance.identify('user-123', firstName: 'Ada');
Call onAppForeground() whenever your app resumes — for example from a WidgetsBindingObserver:
@override
void didChangeAppLifecycleState(AppLifecycleState state) {
if (state == AppLifecycleState.resumed) {
EngageInApp.instance.onAppForeground();
}
}
To play full-screen video takeovers, hand the SDK a player. It ships without a video dependency, so you choose the package (for example video_player) and the SDK renders whatever widget you return:
EngageInApp.instance.init(EngageInAppConfig(
publishableKey: 'pk_your_key',
navigatorKey: navigatorKey,
videoBuilder: (context, media) => MyTakeoverVideo(
url: media.url,
autoplay: media.autoplay,
loop: media.loop,
muted: media.muted,
),
));
The widget is laid out to fill its slot, so size it to cover (e.g. wrap the player in a FittedBox with BoxFit.cover). Without a videoBuilder, video takeovers render media.posterUrl instead — text, buttons, and reporting all still work.
Requires Flutter 3.27+. See the Flutter SDK reference for the full API.
6. (Optional) Identity verification
A publishable key authenticates your app, not the specific user. To stop a malicious client from passing another user's external_id, enable Settings → In-app identity verification. Your backend computes an HMAC digest and the SDK sends it as the X-Identity-Hash header:
// On your server (never ship the secret to the client):
// hash = hmacSha256Hex(secret, externalId)
// Web:
await EngageInApp.identify('user-123', { identityHash: serverComputedHash });
// Flutter:
await EngageInApp.instance.identify('user-123', identityHash: serverComputedHash);
When verification is required, requests with a missing or invalid hash are rejected.
7. Test & troubleshoot
- Send yourself a campaign through the In-App channel, then open your app and call
onAppForeground(). - Nothing shows? Confirm you called
identify()first, that a takeover isn't already on screen, and that a fetch isn't already in flight —onAppForeground()no-ops in those cases. - Takeovers expire after the channel's lifetime if never fetched — reduce or extend it under the channel's display rules.
- Delivered / shown / clicked events appear in your campaign analytics.
Related
Web SDK reference
API reference for @engageapp/inapp-web — init, identify, onAppForeground, setHandlers, and dismiss.
Flutter SDK reference
API reference for engageapp_inapp — navigator-key setup, init, identify, onAppForeground, and setHandlers.
Public API reference
The public HTTP API for server-side and SDK integrations — authentication headers and endpoints.
Channels overview
The delivery channels EngageApp supports — email, SMS, WhatsApp, push, custom API, and full-screen in-app takeovers.