> ## Documentation Index
> Fetch the complete documentation index at: https://docs.apostra.com/llms.txt
> Use this file to discover all available pages before exploring further.

# HTML5 and display tag creative requirements

> What an HTML5 ZIP or a third-party display tag (CM360) needs so a Google Ad Manager seller accepts it: one click tag, a declared size, a backup image, and no unfilled CM360 placeholders.

An HTML5 creative is a ZIP of HTML, scripts and images. A seller's ad server
reads the ZIP when Apostra sends it, and it turns away a ZIP it cannot make
clickable. That refusal arrives after you have saved the creative and moved on,
so check these requirements before you upload.

## Declare exactly one click tag

An ad server finds the creative's exit, the place a click goes, from a plain
JavaScript declaration in an HTML file in the ZIP. Google Ad Manager works this
way, and so do most ad servers. Declare one click tag in the main HTML file and
use it for the exit:

```html theme={null}
<script>
  var clickTag = "https://example.com";
</script>
<img src="banner.png" alt="" onclick="window.open(clickTag)" />
```

The declaration has to match this form:

* It starts with `var`, then the name `clickTag`, optionally followed by digits
  (`clickTag`, `clickTag1`, `clickTag2`). The name is case-sensitive.
* It assigns a quoted string on one line, in double or single quotes. An empty
  string is fine, because the approved click-through replaces the value.
* It is in an `.html` or `.htm` file. A declaration in a `.js` file is not
  found.

These forms are not detected. A ZIP that uses only them has no click tag, as far
as the ad server is concerned:

| Form | Why it is not detected |
| - | - |
| `window.clickTag = "https://example.com";` | It is not a `var` declaration. |
| `var clickTag;` | It declares no value. |
| `var clickTAG = "…";` | The name is case-sensitive. |
| `var clickTag = new URLSearchParams(location.search).get("clickTag");` | The value is computed, not written in the declaration. |
| `var clickTag = "…";` in a `.js` file | Only HTML files are read. |

Declare one exit. A ZIP that declares two different click tags, for example
`clickTag` and `clickTag1`, has two exits, and one approved click-through cannot
fill both. Repeating the same `clickTag` declaration in more than one HTML file
is one exit.

### The approved click-through replaces the declared value

The URL you wrote in `var clickTag` is not the final destination. When a
Google Ad Manager seller receives the ZIP, its sales agent writes the
click-through approved on the creative into the one click-tag declaration and
leaves every other part of the ZIP unchanged. Use any valid `https://` URL while
you build the ZIP, and set the real destination on the creative.

An ad server accepts a click-through only when it is a valid URI, so characters
it would reject, such as spaces, braces and non-ASCII letters, are
percent-encoded when the value is written. The URL decodes to the same
destination.

### When Apostra warns you

When you bring an HTML5 ZIP to Apostra, whether you attach it in chat or upload
it through the v2 buyer upload flow, Apostra opens the ZIP's HTML files and
checks for a click tag. V3 `save_creative` does not take a ZIP. If the check
finds no click tag, or more than one different click tag, the ZIP is still saved
and the reply carries a warning. The warning names the problem, says what to
add, and links this page. It is a warning rather than a refusal because some
sellers use another click mechanism. A seller whose ad server needs a detectable
click tag, such as Google Ad Manager, still refuses the ZIP.

Fix it by adding the declaration above to the main HTML file and saving the
corrected ZIP to the creative. Apostra checks only a ZIP it receives in the
upload itself. It does not download a ZIP you linked by URL.

## Declare the size

Every HTML5 creative needs a size, because a seller that lists one format per
banner size matches the creative to a format by its size. Put it in the main HTML
file:

```html theme={null}
<meta name="ad.size" content="width=300,height=250" />
```

Use the banner's real pixel size. Apostra does not infer it from the file name,
the CSS or the images. A creative with no size is refused when you save it, and
nothing is saved. The ways to declare a size, including `formatParams`, are in
[Save an HTML creative with its size](https://docs.apostra.com/v3/manage-ads.md#save-an-html-creative-with-its-size).

## Add a backup image when the seller asks for one

Some sellers' HTML5 formats require a backup image, the still picture shown
where HTML5 cannot run. Where a seller's format requires one, the creative needs
an image asset in the `backup_image` slot, and the format can also set a maximum
file size for it. A creative with no backup image, or one over the limit, does
not fit that format. Add the image to the creative and save it again.

## Third-party display tags (CM360 tags) for Google Ad Manager sellers

A tag from Campaign Manager 360 (CM360) can carry bracket placeholders such as
`[timestamp]` or `[gdpr]`. Google Ad Manager (GAM) expands only its own `%%…%%`
macros, so a CM360 placeholder reaches the page as written unless the seller's
sales agent handles it first. For a GAM-backed seller the sales agent handles an
inline tag's placeholders like this:

| Placeholder in the tag | What happens |
| - | - |
| `[timestamp]` | Filled with GAM's `%%CACHEBUSTER%%`, so it is a fresh value on every request. |
| `[APIFRAMEWORKS]`, `[OMIDPARTNER]` | Passed through unchanged. They are optional in-app signals that only an app SDK can fill, and CM360 serves the ad without them. |
| Any other unfilled CM360 placeholder: `[gdpr]`, `[gdpr_consent_755]`, `[addtl_consent]`, `[gpp]`, `[gpp_sid]`, `[dc_rdid]`, `[dc_lat]`, `[tag_for_child_directed_treatment]`, `[tfua]`, `[ltd]`, `[dc_tdv]`, `[BREAKPOSITION]`, `[keyword]`, `[url_encoded_publisher_data]` | The creative is refused before it is uploaded to GAM, with the reason code `CM360_UNFILLED_PLACEHOLDER` naming the placeholders. GAM has no exact equivalent to fill them with. |

Fill or remove a refused placeholder in the tag, then save the corrected tag. The
refusal appears on the creative's seller entry, as
[Creative sync outcomes](https://docs.apostra.com/v3/creative-sync-outcomes.md) describes.

A GAM seller accepts a display tag in two forms, once the seller's product
declares the delivery type in `supported_delivery_types`:

* `inline_markup`: the tag's markup, sent unchanged as a third-party creative.
* `paired_redirect`: the tag's ad-request URL with its click-through URL. When
  the ad-request URL is a CM360 `https://ad.doubleclick.net/ddm/ad/…` URL, GAM
  runs it as an internal redirect, and CM360 handles clicks. Any other
  ad-request URL is sent as an image redirect with its click-through URL.

Saving a display tag is described in
[Bring data in](https://docs.apostra.com/v3/bring-data-in.md).

## Related

* [Creative sync outcomes](https://docs.apostra.com/v3/creative-sync-outcomes.md) shows where a creative
  stands on each seller, and why a seller did not accept it.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.