Why "the ad looks right but nothing happens on tap" is the most common failure

A playable can render perfectly — animations play, the game loop works, the end card shows — and still fail at the one moment that actually matters commercially: the tap. Rendering and click-through are handled by entirely different code paths. Rendering is just HTML, CSS, and JS running in a WebView. Click-through requires calling a specific function that only exists because a specific network's SDK or ad server injected it into that WebView at runtime.

If the build calls the wrong function, calls it before that function exists, or never wires it to the CTA element at all, the result looks identical to a working ad in every way except the one that generates revenue. Nothing throws a visible error in most cases — the tap just does nothing. That silent failure mode, repeated across every network with its own convention, is why click-through wiring is the single highest-frequency issue in playable ad production.

Every network, its own API

This table is sourced directly from PlayableKit's own network specification — the same source of truth its packaging pipeline reads from when it builds a per-network output.

NetworkClick-through APIGame-end / other callback
Google AdsExitApi.exit()
Meta (Facebook)FbPlayableAd.onCTAClick()
AppLovinmraid.open(url)
Mintegralwindow.install() fallback to mraid.open(url)window.gameEnd()
TikTokwindow.openAppStore()
Unity Adsmraid.open(url)
Vungle (Liftoff)window.parent.postMessage('download','*')

Notice that no two networks' primary call are the same function, even where the underlying mechanism overlaps. AppLovin and Unity Ads both happen to land on mraid.open(url), but that's coincidence rather than coordination — both simply chose to implement the MRAID standard's open call rather than invent their own. Every other network went its own way.

The underlying pattern: standard MRAID vs. proprietary SDK

Squint at the table and it splits into two camps:

  • Networks that lean on the MRAID standard. AppLovin and Unity Ads both require mraid.open(url) — the IAB's Mobile Rich Media Ad Interface Definitions call for opening a landing page or store listing. Mintegral also falls back to it, but only when its own SDK-native function isn't present. Vungle's documented fallback is MRAID open as well, behind its primary postMessage call.
  • Networks that require their own SDK-native function. Google injects ExitApi from its own hosted script and expects ExitApi.exit(). Meta expects FbPlayableAd.onCTAClick() from its own bridge. TikTok expects window.openAppStore() from its Playable SDK. Mintegral's preferred call, window.install(), is also SDK-native — MRAID is its fallback, not its primary. Vungle's primary CTA, window.parent.postMessage('download','*'), is a raw postMessage to the parent frame rather than any named API at all.

MRAID exists precisely to solve this problem — an open, cross-network standard meant to let one creative work across any MRAID-compliant container without custom code per network. If every network had adopted it fully, this comparison wouldn't need to exist. In practice only two require it outright, and even Mintegral and Vungle only treat it as a secondary path.

Why every network invented its own convention anyway

None of this is arbitrary — each network had a real reason to diverge. Attribution is business logic, not rendering logic: a generic mraid.open(url) call just opens a URL — it doesn't tell Google's ad server, Meta's Ads Manager, or TikTok's dashboard which specific ad, campaign, and impression that click belongs to. A network's own SDK-native call wires straight into its own attribution pipeline before the click ever reaches the store; MRAID has no concept of any individual network's ad ID scheme. Timing also played a role: networks were already running production playable formats with their own click handling before MRAID matured through its own versions, and retrofitting an entire review pipeline onto a later-arriving standard is a bigger lift than keeping what already works. Some delivery paths genuinely need different runtimes — Mintegral's two-call requirement exists because the same creative can be served through its own direct SDK integration (which injects window.install()) or through programmatic/exchange delivery via a generic MRAID container (which only offers mraid.open()); no single function could cover both. And plainly, a proprietary click API is also a proprietary measurement API — a network that owns the full loop from impression to attribution has limited incentive to hand that off to a standard it doesn't control.

What this means if you're wiring click-through by hand

Building or porting one playable across every network by hand means the CTA button's click handler can't be a single function — it has to branch per network build, or per detected runtime, and get every branch right:

  • Google needs clickTag declared in global scope plus ExitApi.exit() wired once exitapi.js has loaded — see Google Ads clickTag not working.
  • Meta needs FbPlayableAd.onCTAClick() called directly, with no window.open or redirect substitute — see FbPlayableAd.onCTAClick() not firing.
  • AppLovin and Unity Ads both need mraid.open(url), but only after MRAID actually reports ready — see AppLovin MRAID not-ready timing.
  • Mintegral needs a runtime check for window.install() at click time, with mraid.open() as the fallback and window.gameEnd() wired separately — see Mintegral window.install() vs mraid.open().
  • TikTok needs its own Playable SDK loaded and window.openAppStore() called from it — see TikTok openAppStore() not working.
  • Vungle needs window.parent.postMessage('download','*') as the primary call, with MRAID open as a fallback only — easy to miss if a build was adapted from an MRAID-first network.

Get any one of these wrong — wrong function, wrong timing, or a handler never attached to the CTA element — and the symptom is identical across every network: the ad renders, the tap does nothing, and there's rarely a console error pointing at the cause.

How PlayableKit handles this from one source export

PlayableKit's packaging pipeline reads the same network specification quoted in the table above and wires the correct click-through call automatically for whichever networks you select, from a single Unity Playworks/Luna export: clickTag plus ExitApi.exit() for Google, FbPlayableAd.onCTAClick() for Meta, MRAID-ready-gated mraid.open(url) for AppLovin and Unity Ads, a click-time window.install()/mraid.open() check plus window.gameEnd() for Mintegral, SDK-loaded window.openAppStore() for TikTok, and window.parent.postMessage('download','*') for Vungle.

Each output is a separately packaged creative built to that network's actual click, asset, and size rules — not one generic build hoping a single click handler covers every case. For the full picture of what else differs per network beyond click-through, see exporting one playable for multiple networks.

Worth knowing: if you already have a build and just want to confirm which click-through call is actually present in the HTML — rather than which one you meant to wire — PlayableKit's Playable Ad Validator checks that for free with no signup, per network.