Why Mintegral has two click-through calls instead of one
Most networks give you exactly one CTA function to call. Mintegral gives you two because a single Mintegral-served creative can actually be rendered by two different runtimes, depending on how the impression was bought:
window.install()is Mintegral's own SDK-native call. It's injected by Mintegral's in-app SDK when the ad is served through Mintegral's direct integration — the mediation path most Mintegral placements actually run through. Calling it fires Mintegral's own click and attribution tracking alongside the store open.mraid.open(url)is the generic IAB MRAID call. It's what's available when the same creative is delivered through a container that only implements standard MRAID and never injects Mintegral's proprietary bridge — typically programmatic or exchange delivery, where a DSP is bidding on Mintegral inventory through an SSP rather than through Mintegral's own SDK.
The HTML itself can't know in advance which of those two runtimes will load it — the same ZIP gets served both ways depending on the buy. So the adapter has to check for the SDK-native function at click time and only fall back to the MRAID-standard one when it isn't there.
The practical rule
Mintegral's own guidance, and the clickApi Playable Kit encodes for the network, reduces to one line: call window.install() if it exists as a function; otherwise call mraid.open(). In practice:
- Check for the function at the moment the CTA is clicked, not once at page load —
window.installmay not exist yet when your script first runs, but be injected later by the SDK bridge. - Test with
typeof window.install === "function", not justif (window.install). Some containers stub the property with a non-function placeholder before the real bridge attaches; a truthy check alone can call something that isn't callable, or silently skip a valid fallback. - Only reach for
mraid.open(url)when the SDK-native call is genuinely absent, and pass the actual store URL — it's the generic path, not the preferred one, so it should only run when Mintegral's own function isn't available.
if (typeof window.install === "function") { window.install(); } else if (window.mraid && typeof window.mraid.open === "function") { window.mraid.open(storeUrl); }
Common mistakes
- Wiring only
mraid.open(). This works fine in testing if you happen to preview through a generic MRAID container, but it means Mintegral's own SDK-native click and attribution tracking never fires when the creative runs through Mintegral's direct integration — the more common delivery path for Mintegral inventory. - Wiring only
window.install(). The CTA button does nothing at all the moment the same creative is served through programmatic/exchange delivery, since that runtime never injectswindow.install. A silently dead button on a subset of impressions is exactly the kind of thing that shows up as unexplained low click-through rate, not an obvious error. - Checking existence incorrectly. Testing with
typeof window.install !== "undefined"instead of=== "function"still passes for non-function stubs. Caching a reference towindow.installin a variable before the SDK bridge has attached also fails silently — the check needs to happen at click time, against the livewindowobject. - Forgetting the game-end callback. Mintegral's spec also expects
window.gameEnd()to fire, and its own review process checks both the app-store click and the game-end callback — fixing only the CTA logic while skippinggameEnd()still leaves a creative that can fail review.
How PlayableKit's generated adapter handles both
PlayableKit's Mintegral output wires this exactly per the network's own two-call spec, at click time rather than at load time:
- On click, it first calls Mintegral's own analytics hook (
pi.logCta()) if present. - It then checks
typeof window.install === "function"and calls it if true. - If
window.installisn't a function, it falls back tomraid.open()with the resolved Android/iOS store URL, and falls back again to a plainwindow.open()if MRAID isn't present either. - On the game-end event, it calls
window.gameEnd()when that function exists, matching Mintegral's requirement to confirm both callbacks.
Because the check happens against the live window object at the moment the player clicks, the same generated package behaves correctly whether Mintegral's own SDK has injected window.install or the creative is running inside a generic MRAID container from a programmatic buy — no manual branching, and no need to know in advance which runtime a given impression will use. PlayableKit's free validator also flags a Mintegral package if none of window.install, mraid.open, or pi.logCta is present in the HTML at all, so a missing CTA gets caught before submission rather than after rejection.