What Meta requires, exactly

Meta's playable spec is narrower than most networks': there's no ZIP option and no bundled-resources folder. Everything has to live inside one file.

  • Single self-contained HTML file — not a ZIP, not an HTML file with a linked assets/ folder. One .html file, full stop. Meta's uploader expects exactly one file (maxFiles: 1).
  • 5 MB maximum, 2 MB recommended — the hard cap is 5 MB, but Meta explicitly recommends staying near 2 MB. Creatives that clear the cap but sit well above 2 MB tend to load slowly in-feed, which affects delivery even when the file technically passes review.
  • Every asset embedded, zero external requests — images, audio, fonts, and any script the playable depends on must be inlined into that single file. Meta's spec explicitly prohibits external JS, CSS, image, font, and network calls of any kind. A creative that fetches even one resource over the network fails review, not just at ad-serving time but often immediately at upload.
  • CTA must call FbPlayableAd.onCTAClick() — this is Meta's own click-through API, and it's not interchangeable with any other network's. No window.open(), no auto-redirect on load, no mraid.open() — the tap-to-install button has to invoke that exact function so Meta's own SDK controls the store redirect.
  • No auto-playing audio before user interaction — sound can only start after the user has actually interacted with the ad, not on load.
RequirementMeta's rule
Output formatSingle HTML file (no ZIP)
Max size5 MB hard cap, 2 MB recommended
Asset loadingAll embedded — external requests prohibited
Click-through APIFbPlayableAd.onCTAClick() only
AudioMust wait for user interaction
MRAIDNot used — MRAID-format creatives are rejected

Why a raw Playworks/Luna export fails this immediately

Unity Playworks/Luna builds a real, working playable and exports it as a ZIP — usually somewhere like ProjectRoot\LunaTemp\stage4\create-hub. That export is designed to run correctly once unpacked, which means its HTML file references images, audio, and data files by relative path inside that folder structure. That's the opposite of what Meta wants.

Two problems show up as soon as you try to submit a raw export to Meta:

  1. It isn't a single file. The entry HTML depends on a folder of sibling assets. Meta doesn't accept a ZIP or a folder — it wants one .html file with nothing external to load. Uploading the exported ZIP, or even just the entry HTML on its own, breaks every asset reference the moment it's opened outside its folder.
  2. There's no FbPlayableAd wiring. Playworks/Luna has no idea your build is headed to Meta specifically — its CTA button, if wired at all, isn't calling FbPlayableAd.onCTAClick(). Whatever click handler exists needs to be replaced with the exact function Meta's review process checks for.

Neither of these is a bug in the Luna export — Playworks isn't building for a specific network at all, and it doesn't have to. The adaptation work is a separate step, and Meta's version of that step is stricter than almost anyone else's on the list: AppLovin and Unity Ads share the "single file, everything embedded" requirement, but Meta adds the network-specific CTA call and a tighter size recommendation on top.

How PlayableKit handles the Meta export

Upload the same Playworks/Luna export ZIP you already have, select Meta as a target, and PlayableKit does the two things a manual conversion requires:

  • Inlines every asset as base64. Images, audio, and any other referenced file get read out of the export folder and embedded directly into the HTML as data URIs, so the result is exactly one file with nothing left to fetch externally — satisfying Meta's prohibitsExternalRequests rule by construction rather than by manual cleanup.
  • Wires the CTA to FbPlayableAd.onCTAClick() automatically. The click-through handler is replaced with the exact call Meta's review checks for — no auto-redirect, no window.open() left over from a different network's build. Audio triggers are checked against the same "no autoplay before interaction" rule.

The output is named meta.html, matches the single-file requirement Meta's uploader expects, and is checked against the 5 MB cap (with a flag if it's over the 2 MB recommendation) before you download it.

Worth knowing: if you want to check size and asset-embedding compliance on a build you already have — before spending a submission attempt on it — PlayableKit's free size checker and playable ad validator both run with no signup.

Meta is one of several targets from the same build

If you're only shipping to Meta, the base64-inlining and CTA rewiring above is the whole job. Most teams running the same creative across networks are also converting the identical Playworks/Luna export for Google Ads and AppLovin at the same time — each with its own click API and packaging shape, generated from that one source build rather than three separate manual conversions. See the broader picture on the Unity Playworks/Luna alternative page.

What it costs

Meta exports are included on every paid PlayableKit plan, starting with Starter at $400/mo (which also covers Google Ads, AppLovin, and Unity Ads). Studio at $560/mo and Scale at $800/mo unlock every network, including Mintegral, TikTok, and Vungle, with higher build limits and more seats. Enterprise pricing is custom for teams that need dedicated infrastructure. Full details are on the pricing page.