What clickTag is, and why Google specifically requires it
clickTag is a global JavaScript variable — not a Google API, just a plain string sitting on window — that holds the destination URL for a playable's click-through. Google's ad server injects its own logic around your creative at serve time and reads that variable to know where to send the user when they tap. If the variable isn't there, isn't a string, or isn't visible in global scope by the time Google's server looks for it, there's nothing to read.
Google layers a second piece on top: the ExitApi script, loaded from tpc.googlesyndication.com. Once loaded, calling ExitApi.exit() is the documented way to fire the click-through, with Google's own click-tracking wrapped around it. PlayableKit's network spec lists this explicitly: for Google, clickApi: "ExitApi.exit()". A compliant build needs both pieces — the clickTag variable for the destination, and a window.open(clickTag) or ExitApi.exit() call wired to the actual tap.
Common reasons clickTag doesn't work
1. Declared in the wrong scope, or too late
var clickTag = "..." inside a function or module wrapper isn't visible to Google's ad server — it needs window.clickTag to resolve. It also needs to exist early: if the declaration is injected near the bottom of the page, or added asynchronously after other scripts run, Google's server can look for it before it exists and come back empty.
2. Multiple, conflicting clickTag declarations
Easy to miss on a build that's been hand-edited more than once. If clickTag gets declared twice — once by an export tool, once by hand, once by a leftover template — whichever assignment runs last wins, and it's often not the one pointing at the right store URL. A stale placeholder from an earlier draft is a common result.
3. Calling exit() without the right setup, or the wrong argument
ExitApi.exit() only works if exitapi.js is actually loaded on the page — calling it before the script tag exists, or when it fails to load, throws or silently no-ops. It's also easy to assume exit() takes a URL argument the way window.open() does; it doesn't. The destination comes from clickTag, not from whatever gets passed into exit().
4. The click handler is never actually wired to a tap
clickTag can be declared perfectly and still do nothing if no button or DOM element in the playable ever calls window.open(clickTag) or ExitApi.exit() on a real user interaction. This shows up most after an engine export: the game renders a CTA button, but nothing in the exported HTML attaches a click/touch listener that reaches the click-through code.
How PlayableKit's ensureGoogleClickTag() handles this
PlayableKit's CLI has a dedicated function, ensureGoogleClickTag(), that runs whenever you package a build for the Google Ads target. It does three things, in order, precisely to avoid the mistakes above:
- Checks for an existing declaration first. It tests the HTML for
var clickTag =orwindow.clickTag =and, if either is already present, leaves the file untouched — so it never creates the double-declaration problem above. - Injects a single global declaration at the top of
<head>. If no existingclickTagis found, it insertsvar clickTag = "...";followed bywindow.clickTag = clickTag;inside a script tag placed right after the opening<head>tag — guaranteeing global scope and that it runs before anything else, fixing the scope and timing failures above. - Sources the URL from your actual store links. The value comes from whichever of
--android-urlor--ios-urlyou passed to the CLI, falling back tohttps://www.google.comonly if neither was supplied — no placeholder URL left behind by accident.
PlayableKit's Google packaging also adds the exitapi.js script tag right before </head> if it isn't already referenced, and wires the CTA to try window.open(window.clickTag) first, then fall back to ExitApi.exit() if ExitApi is present — so the click-through fires correctly without a placeholder URL or a dangling function call.
ensureGoogleClickTag() only injects when it doesn't already see a clickTag declaration. If clicks still aren't working after a hand edit, that's the first thing to check — a leftover or malformed declaration blocks PlayableKit's automatic fix from applying at all.
If you're hand-editing: what to check
- Search the file for
clickTag— there should be exactly one assignment, in global scope, near the top of<head>, with a real store URL, not a placeholder. - Confirm the
exitapi.jsscript tag fromtpc.googlesyndication.comis present if anything callsExitApi.exit(). - Open the build and actually click the CTA — confirm a listener is attached to a real tap/click event and calls
window.open(clickTag)orExitApi.exit(), not just an engine-internal event. - Run it through a validator before upload, so a broken clickTag doesn't cost a full Google review cycle.