What window.openAppStore() actually is

On most networks, the click-through call is a generic convention: AppLovin and Unity Ads use MRAID's mraid.open(url), Vungle posts a message to its parent frame, Google reads a clickTag variable. window.openAppStore() is not that kind of call. It's a function that TikTok's own Playable-sdk.js attaches to the global window object at runtime — it doesn't exist in the page at all until that script has loaded and executed.

That distinction matters because it changes where the failure comes from. An MRAID call fails if the ad network's host app never injects an mraid object — that's outside your control. window.openAppStore() fails for a much more mundane reason: the SDK script tag that defines it is missing, points to the wrong path, or runs after the code that tries to call it. All three produce the exact same symptom — a tap that does nothing, with no error banner and usually no visible console warning unless you're specifically checking for one.

Common specific mistakes

  • The SDK script tag is missing entirely. This is the most common cause by far, because it usually isn't an oversight — it's a reused build. A package assembled for AppLovin, Unity Ads, or Mintegral never had a reason to include Playable-sdk.js, so when the same HTML gets repointed at TikTok with only the CTA call swapped to openAppStore(), the function it's calling was never defined anywhere in the document.
  • The script tag is present but placed after the creative's own JS. Browsers execute <script> tags in document order. If the adapter code that wires up the click handler sits before the <script src="…/Playable-sdk.js"> tag, window.openAppStore is still undefined at the moment the handler is defined and bound — even though the SDK loads correctly a moment later.
  • The function is called before the SDK finishes initializing. Even with the tag in the right position, a remote script still has to fetch and execute before its globals exist. Binding the click handler directly on page load — rather than after a ready signal from the engine bridge — creates a race: on a fast connection it usually works, on a slow one the tap fires into a function that isn't there yet.

A quick way to confirm which of these you're looking at: open the packaged index.html in a text editor and search for Playable-sdk.js. If it's absent, that's cause one. If it's present, check whether it appears before or after the script block that defines the click handler — that's cause two. If the order looks correct, open the creative in a browser, open devtools, and tap the CTA immediately versus after a short delay; if it works on the second attempt but not the first, that's cause three.

The fix

All three causes resolve the same way: get the SDK script tag onto the page, in the right place, and don't call the function until the SDK has actually had a chance to run.

  • Add the SDK script tag if it's missing. The document needs a <script src="…/Playable-sdk.js"> pointed at TikTok's hosted SDK, loaded locally as part of the package rather than assumed to already be present from another network's build.
  • Put it before the adapter script, not after. Because script tags execute in document order, the SDK tag needs to appear earlier in the <head> or <body> than the inline script that defines and binds the click handler. That ordering alone is what guarantees window.openAppStore exists by the time the adapter code runs.
  • Guard the call and wait for a ready signal. Wrap the call in a typeof window.openAppStore === "function" check rather than calling it unconditionally, and bind the click handler to the engine's own "build ready" event instead of firing it at parse time — that way the handler only becomes active once the rest of the runtime, including the SDK, has actually initialized.

This is exactly the ordering PlayableKit enforces automatically: when converting a build for TikTok, it injects the Playable-sdk.js tag first, then an adapter script immediately after it that defines openAppStore() as the CTA and only binds it once the engine signals it's ready to accept input — so the script-order and premature-call failure modes above can't happen in the generated package.

Before you re-upload: run the package through PlayableKit's Playable Ad Validator — it specifically checks for the presence of Playable-sdk.js and an openAppStore reference in the packaged HTML, which catches the missing-script-tag case before TikTok's review does.