Free tool · no sign-up

Adaptive Creative validator

For Vungle and Liftoff Monetize playables — three names for one format

Drop in the ZIP you would submit to Vungle or Liftoff Monetize and watch what your creative posts to the parent frame. Adaptive Creative has no SDK object to inspect — its whole contract is two messages, download and complete, and the rule that they must never both fire. A creative opened on your own machine posts them into nothing and looks fine either way, which is exactly why they are worth watching land on a timeline.

Drop a playable to run it

The .zip you would submit to Vungle or Liftoff Monetize, or a single .html build. It runs inside a mock Adaptive Creative container in a sandboxed frame on this device — nothing is uploaded.

How Adaptive Creative works

Vungle, Liftoff Monetize and Adaptive Creative are one thing

Vungle was acquired in 2021 and the network now trades as Liftoff Monetize, but both names remain in circulation and advertisers arrive knowing one or the other. Adaptive Creative is the name Liftoff's own documentation gives the format — a playable, standalone or paired with a video, whose ad logic is kept separate from the creative so that a single package runs across every platform they serve. Whichever of the three names brought you here, this is the right page, and the file you submit is the same file in all three cases.

There is no SDK object to look for

This is the difference that shapes everything else, and it separates Adaptive Creative from every other framework a playable might target. MRAID gives you an mraid object. Meta gives you FbPlayableAd. Mintegral installs six functions in your window. Adaptive Creative gives you nothing at all — the interface is window.parent, which exists in every frame ever created, whether or not a single line of code on the other side is listening. Your creative cannot ask whether it is running inside Liftoff, because there is no question available to ask.

Which is why a broken build looks perfect on your machine

A creative that posts a message to nobody throws no error, logs no warning and returns no value. Open your playable by double-clicking it and the call-to-action will appear to work — the tap registers, the handler runs, the message departs, and nothing anywhere reports that it arrived at an empty room. There is no equivalent of an MRAID method returning undefined or a Mintegral loading screen refusing to lift. The failure is completely silent by design, and the only way to see these two events is to put something on the other side of the frame that listens for them.

Two messages, and the danger is that both are exits

download is sent when the viewer asks to go to the store, normally from the call-to-action or an end card. complete is sent once the ad has been played far enough to count. The natural reading is that one is a click and the other is a progress milestone, and that reading is what causes the failure: Liftoff's own wording is that complete guarantees calling download automatically. It is not a notification that the round ended. It is a second route to the store, taken on your creative's behalf, and a playable that sends both hands the same viewer to the app store twice.

So the rule is a latch, not an ordering

Liftoff's guidance states that the two events should never trigger together, and the safe way to honour that is not to be careful about sequencing — it is to make the second send impossible. One flag, set by whichever event goes first, checked by both. Worth noting that it should never reset, including when a player restarts: someone who has already been handed to the store has left the ad, and a fresh round beginning is not fresh permission to send them again. The exporter behind this tool latches exactly once and never clears it, which is the behaviour this container checks for by letting you try to fire both.

Their verifier reads your file, and it reads it literally

The part that costs people a submission cycle, and the part no specification prepares you for. Liftoff's Creative Verifier does not run your ad to discover whether it exits — it searches the source text for the call, spelled out. A build exported from this editor was rejected with the message that parent.postMessage('download','*') was not found, and that rejection was correct about the file while being entirely wrong about the behaviour: the build posted download properly on every single tap. It had simply composed the message from a variable, and a scanner reading text cannot see a string that only exists once the code has run.

Write both calls out, twice, even though it looks redundant

The consequence of the paragraph above is a piece of code that every instinct says to refactor. Two postMessage calls that differ by one word want to become one call taking an argument, and the moment they do, the scanner stops finding either of them. Write them separately, as literals, quoted and spaced the way the verifier spells them — and then make sure nothing downstream undoes that. A minifier that rewrites quotes or a bundler that hoists the string will reintroduce the same rejection long after you stopped thinking about it, which is why the exporter here splices this block in unminified on purpose.

Package it as a ZIP whose entry is index.html

Liftoff's written specification has historically named the entry document ad.html, and their Creative Verifier disagrees with it — the tool refuses a bundle whose entry is called anything else, and tells you in as many words to rename ad.html to index.html before testing can resume. The verifier is the one that decides, because it runs the same scan the review pipeline runs, and a bundle it will not open is a bundle nobody can QA before submission. Where several HTML files sit at the root of the archive, index.html is what marks the one to start from.

The two events, and why both are exits

The whole contract is two posted messages. They read like a click and a progress milestone, and that reading is what fails submissions — each one sends the viewer to the store, so a creative that fires both sends the same person twice.

downloadSends the viewer to the storeon the call-to-actionparent.postMessage('download','*')

The explicit exit, sent when someone taps your call-to-action or the button on an end card. This is the one people implement first and usually implement correctly. The two things that go wrong are spelling — a message assembled at runtime passes every test you can run yourself and fails the source scan — and firing it after complete has already gone, which sends a viewer who has left a second time.

completeAlso sends them to the storeonce the ad has been played far enoughparent.postMessage('complete','*')

Read the column to the left twice, because this is where submissions fail. complete is not a progress report and not an end-of-round signal — Liftoff describe it as guaranteeing the download call automatically, so sending it is sending the viewer to the store just as surely as the call-to-action does. Fire it when the playable has genuinely been consumed, from whatever your ad treats as its finish, and only if download has not already gone.

One latch, never reset. Whichever event fires first should make the other impossible — including across a retry. A viewer handed to the store has left the ad, and a second round beginning is not permission to send them again.

Six ways an Adaptive Creative fails review

Every one of them runs correctly in a browser opened directly. Posting a message to a frame nobody is listening on produces no error, so there is no local signal at all — which makes these the most expensive failures of any framework here to find another way.

Rejected with a message saying the download call was not found

The most confusing rejection in this framework, because it is a true statement about your file and a false one about your ad. The verifier greps the source for the call written out; your build composed it from a variable, a helper, or a constant holding the word. It worked flawlessly every time you tested it and was never executed during the check. Write both calls as literals, and check what your minifier does to the quotes before you resubmit.

Rejected for sending the viewer to the store twice

download and complete both fired. Usually because complete was treated as an end-of-round notification and wired to the same place a game-over screen appears, so tapping the call-to-action on that screen sent the second one moments later. Both are exits. One latch, set by whichever goes first and never reset, is the fix — and the timeline above will show you the pair landing if your build has this.

Everything works locally and nothing happens in review

The silent failure this whole page is about. Posting a message to a parent frame that is not listening produces no error, so a creative with a misspelled event name, a handler that never runs, or an exit routed through window.open instead behaves identically to a correct one when you open it yourself. There is no local signal at all. A listening container is the only thing that can tell the two apart, which is what the tool above is.

The bundle is refused before anyone reviews the creative

A packaging failure rather than a runtime one, and the quickest of these to fix. The archive needs a file called index.html at its root — if yours is named ad.html, following the older written specification, the verifier will not open it and will say so. Rename it and testing resumes. The 5 MB cap is measured on the archive as uploaded, and artwork is what usually pushes a build past it.

The ad never sends complete at all

Less visible than the others, because nothing is rejected and nothing looks broken — the ad simply never reports that it was consumed. It happens when a creative has no definite finish, or when the finish exists but nothing is wired to it. Decide what counts as far enough through your playable, fire complete there, and let the latch handle the case where the viewer had already tapped through.

Tapping the call-to-action opens a new tab in review

The exit went through window.open or a plain anchor rather than a posted message. This is the failure mode of a build made for another network and retargeted — an MRAID creative exiting through mraid.open, or a web preview build that was never converted. Navigation of this kind is what every network on this list forbids, and it is the one mistake that is more visible inside a container than outside one.

Liftoff runs a verifier of their own

Their Creative Verifier installs on a test device and previews your bundle inside the real Liftoff Monetize SDK. It is the authority on whether a build will be accepted — this page is for catching the mistakes before you get there, and for explaining what its messages mean when it refuses one.

Liftoff Creative Verifier

Vungle and Liftoff are two of the thirteen networks this editor exports to. If your creative targets Unity, AppLovin, ironSource or Chartboost, use the MRAID validator instead; for Mintegral there is the Mintegral validator, and for size limits and packaging across all of them, read the network specs.

Open the spec checker

Common questions

Is my creative uploaded to a server?
No. The file is read in your browser and rendered into a sandboxed frame on this device. Nothing is transmitted and nothing is stored — you can confirm it by watching your browser's network tab during a run, or by disconnecting after the page loads and running one anyway.
Why is this called the Adaptive Creative validator and not the Vungle validator?
Because Adaptive Creative is the name Liftoff give the format, and one page covering it serves both names the network trades under. Vungle became Liftoff Monetize after the 2021 acquisition, advertisers still arrive knowing either one, and the creative you build is identical in both cases. The tool is listed under the network names wherever you might be looking for it, and titled with Liftoff's own word here.
What is the difference between download and complete?
Less than you would expect, and that is the point. download is the explicit exit, sent when the viewer taps your call-to-action. complete says the ad has been played far enough to count — but Liftoff's wording is that it guarantees calling download automatically, so it is also an exit. Two different reasons to send someone to the store, not a click and a milestone. That is why they must never both fire.
Can a creative tell whether it is running inside Liftoff?
No, and this is the structural difference from every other framework a playable might target. MRAID injects an object your creative can test for, Meta provides FbPlayableAd, Mintegral installs functions in your window. Adaptive Creative posts to window.parent, which exists in every frame regardless of whether anything is listening on the other side. A build cannot detect its own container, so it cannot adapt to running outside one.
Why does this tool check my source as well as running my ad?
Because their Creative Verifier does, and it is the check that fails builds which work perfectly. The verifier searches your file for the calls written out rather than executing your ad to see whether they fire. A creative that assembles the message from a variable passes every behavioural test and fails the scan, which is precisely the rejection that produced this page. Running your ad alone would report a clean pass on a build about to be refused.
Should I upload the ZIP or a single HTML file?
The ZIP, because that is what their verifier takes — it is explicit that the tool works on a bundle rather than a loose file. Upload the archive exactly as you would submit it and the validator opens it, finds the entry document and runs that, while checking the things only an archive can be wrong about: whether the entry is named index.html and whether the whole bundle is under the 5 MB cap. A single HTML file is accepted too, since that is what you have after unzipping one to look inside, but those packaging checks are skipped and reported as untested.
My entry file is called ad.html. Is that wrong?
It is what Liftoff's written specification has historically asked for, and their Creative Verifier will not accept it — the tool refuses a bundle without index.html at the root and tells you to rename ad.html before testing continues. Treat the verifier as the authority, because it runs the same scan the review pipeline runs. Rename the file, keep everything else as it is, and both the tool and the submission will open your creative.
How close is this to Liftoff's real review?
Close enough to expose the mistakes that matter, and deliberately not presented as more than that. It listens for the two messages the way a real container does, checks your source the way a scanner searching for literal calls would, and inspects the bundle for the entry filename and size. What it cannot reproduce is their exact timing, their verifier's wording, or a human reviewer's judgement about whether your ad is any good. Treat a pass here as a good sign and their own Creative Verifier as the authority.
My creative works when I open it directly. Why would it fail review?
Because opening it directly cannot test the only thing that matters here. With no container listening, every posted message vanishes silently and a correct build is indistinguishable from one with a misspelled event, a missing handler, or no exit at all. Nothing errors and nothing warns. The success you saw was the absence of a test rather than the result of one — which is a sharper version of the same problem every framework has, and the reason this tool exists.
Which other networks work this way?
None of the ones this editor exports to, which is why Adaptive Creative has a page of its own. Unity, AppLovin, ironSource and Chartboost use MRAID. Meta, Moloco and InMobi share a single FbPlayableAd.onCTAClick() call, with no events in either direction, and have a validator of their own. TikTok and Pangle use another format again, Mintegral has a two-way protocol with six functions, and Google asks for an exit function and a meta tag. Vungle and Liftoff are the only pair here whose interface is a posted message, and a build made for any other network will fail here on the exit check alone.

Your next playable is one afternoon away.

Open the editor, pick a format and start placing objects. Nothing to install, and nothing to pay.