> ## 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.

# Landing-page macros

> Which {MACRO} tokens Apostra fills in a creative's landing page URL and click-URL parameters, which vendor tokens it rewrites for you, and which it refuses at save with the reason and the fix.

A landing page URL, and the click-URL parameters you add to it, can carry tokens
that stand for a value only known when someone clicks: the cache-buster, the
page's domain, whether GDPR applies. Apostra wraps the landing page in its click
tracker. The seller's ad server fills each token on that tracker, and Apostra's
click redirect copies the filled value into your landing page URL.

That only works for a token the tracker has a parameter for. A token it has none
for would reach your landing page empty or as written, and your analytics would
record a blank where you expected a value. So Apostra checks every token when you
save, and either carries it or refuses the save and tells you which token and
why. It never accepts a token and drops it later.

This applies to the landing page URL (`landing_page_url` on `save_creative`) and to
click-URL parameter values (`macroAdditions` on a creative,
`tracking.macroAdditions` on an advertiser or campaign). For how click-URL
parameters work in general, see
[Click-URL parameters](https://docs.apostra.com/v2/object-guides/advertiser.md#click-url-parameters).

## Tokens Apostra fills

| Token | Filled | By |
| - | - | - |
| `{MEDIA_BUY_ID}`, `{CREATIVE_ID}`, `{CAMPAIGN_ID}`, `{PACKAGE_ID}` | When the creative is sent to a seller for a media buy. `{MEDIA_BUY_ID}` is your media buy's own id, the `mb_…` id the API returns. `{PACKAGE_ID}` has a value only when the creative is on exactly one package | Apostra |
| `{CACHEBUSTER}`, `{TIMESTAMP}`, `{DOMAIN}`, `{APP_BUNDLE}`, `{APP_NAME}`, `{PAGE_URL}` | On each click | The seller's ad server |
| `{GDPR}`, `{GDPR_CONSENT}`, `{US_PRIVACY}`, `{LIMIT_AD_TRACKING}` | On each click | The seller's ad server |
| `{COUNTRY}`, `{REGION}`, `{CITY}`, `{ZIP}`, `{DMA}`, `{LAT}`, `{LONG}` | On each click | The seller's ad server |
| `{DEVICE_TYPE}`, `{DEVICE_MAKE}`, `{DEVICE_MODEL}`, `{OS}`, `{OS_VERSION}`, `{DEVICE_ID}`, `{DEVICE_ID_TYPE}` | On each click | The seller's ad server |
| `{PLACEMENT_ID}`, `{FOLD_POSITION}`, `{AD_WIDTH}`, `{AD_HEIGHT}` | On each click | The seller's ad server |
| `{VIDEO_ID}`, `{VIDEO_TITLE}`, `{VIDEO_DURATION}`, `{VIDEO_CATEGORY}`, `{CONTENT_GENRE}`, `{CONTENT_RATING}`, `{PLAYER_WIDTH}`, `{PLAYER_HEIGHT}`, `{POD_POSITION}`, `{POD_SIZE}`, `{AD_BREAK_ID}` | On each click | The seller's ad server |

Whether a seller fills a given token is up to that seller. A token the seller
does not fill is removed from the landing page URL rather than left as written.

## Vendor tokens Apostra rewrites for you

A token in another platform's syntax is rewritten to the AdCP token when you save.
The URL you wrote is kept as written; the click tracker carries the rewritten form.

| You write | Apostra saves |
| - | - |
| `${GDPR}` | `{GDPR}` |
| `${GDPR_CONSENT_755}`, a consent token that names a vendor | `{GDPR_CONSENT}`, with the vendor (755) kept for the seller |
| `[CACHEBUSTING]`, `%%CACHEBUSTER%%` | `{CACHEBUSTER}` |
| `[timestamp]` inside a Campaign Manager 360 URL (`ad.doubleclick.net`) | `{CACHEBUSTER}` |

For example, a Campaign Manager 360 click tracker landing such as
`https://ad.doubleclick.net/ddm/trackclk/N123.456/B789;gdpr=${GDPR};gdpr_consent=${GDPR_CONSENT_755};ltd=`
reaches the seller with `gdpr={GDPR}` and `gdpr_c={GDPR_CONSENT}` on Apostra's
click tracker, so the seller's ad server fills both.

The AdCP token `{GDPR_CONSENT}` names no vendor, and Google Ad Manager's consent
macro needs one. So for a seller that writes to Google Ad Manager, Apostra's
click tracker declares the vendor your landing page names: its
`macro_declarations` ask the seller to write Google Ad Manager's own token,
`${GDPR_CONSENT_755}`, in place of `{GDPR_CONSENT}`. The seller sends
`gdpr=${GDPR}&gdpr_c=${GDPR_CONSENT_755}` to Google Ad Manager, which fills both
on each click. When your landing page names no vendor, a seller that writes to
Google Ad Manager receives no consent parameter, because Google Ad Manager could
not fill it.

Any other seller receives `{GDPR_CONSENT}` to fill itself, unless the seller's
format option lists the macros it fills and consent is not among them. Apostra
then removes the consent parameter from its click tracker, because that seller
would not fill it, and the landing page receives no consent string. The same rule
applies to every serve-time token on Apostra's trackers.

Apostra's click tracker has one consent parameter, so a landing page can name
one consent vendor. Name the vendor in the landing page URL, not in a click-URL
parameter: a parameter is saved with its consent token rewritten to
`{GDPR_CONSENT}`, so its vendor would be lost (see the table below).

Braces around any other name, such as `{sku}` or `{PRODUCT_ID}`, are left exactly
as you wrote them.

## Tokens Apostra refuses at save

A save with any of these is refused with a `VALIDATION_ERROR`. The error names each
token, the reason, and what to do. Nothing is saved.

| Reason | Tokens | What to do |
| - | - | - |
| `keyless_universal_macro` | Every AdCP macro not in the table above, for example `{KEYWORDS}`, `{REFERRER}`, `{GPP_STRING}`, `{GPP_SID}`, `{IP_ADDRESS}`, `{USER_AGENT}`, `{CLICK_URL}`, and the item and catalog macros such as `{SKU}` and `{ITEM_NAME}` | Remove it. Apostra's click tracker has no parameter that carries it to the landing page |
| `scope3_extension_macro` | `{TRACKING_TAG_ID}`, `{DATA_SET_ID}`, `{TACTIC_ID}`, `{SALES_AGENT_ID}`, `{ATTRIBUTION_TRACKING_ID}` | Remove it. Nothing fills these in a click URL. Only `{CAMPAIGN_ID}` is filled |
| `unsupported_vendor_token` | A `${...}`, `%%...%%` or `[...]` token Apostra cannot map to an AdCP macro, for example `${GPP}`, `%%SITE%%`, `[BREAKPOSITION]`, or `${GDPR_CONSENT}` with no vendor id | Write the AdCP token instead, such as `{GDPR}` or `{DOMAIN}`, or remove it. For consent, write `{GDPR_CONSENT}` |
| `consent_vendor_not_carried` | Consent tokens that name two or more vendors in one landing page URL, such as `${GDPR_CONSENT_755}` and `${GDPR_CONSENT_91}`, or a consent token that names a vendor in a click-URL parameter value | Keep one vendor's consent token in the landing page URL. In a click-URL parameter, write the vendor-less `{GDPR_CONSENT}` and name the vendor in the landing page URL |
| `double_brace_template` | `{{DOMAIN}}` and any other `{{...}}` | Write `{DOMAIN}`, with an AdCP macro name, or remove it |

## Creatives saved before this check

A creative saved before this check keeps working as it was saved. Apostra does not
rewrite its landing page URL or parameters. A save that sends a landing page URL
or click-URL parameters again, even unchanged, is checked like a new one. If that
refuses a creative that was already live, apply the fix in the table and save
again.


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