---
title: "Install the landing-page helper"
description: "Paste one block on the advertiser's site so the click id is stored first-party and conversions fire with easq('track', 'event') — get the block, read it and install it in plain HTML or Google Tag Manager."
type: "howto"
audience: [network-manager, advertiser]
keywords: [landing page helper, snippet, easq, easq.js, easclid, click id, first-party storage, track event, easq track, easq debug, easq get clickid, payout, payout is not value, once per click, already-tracked, arbitrate, last-click, Google Tag Manager, GTM, Safari, ITP, tracking prevention, click id lifetime, reason code, XD-reason, conversion not counted]
ui_path: "Account → Advertisers → advertiser → campaign → Conversion actions → Conversion tracking code → Snippet"
---

Fetch the complete documentation index at: https://epom.com/ad-server/docs/llms.txt. Use this file to discover all available pages before exploring further. Append .md to any documentation page URL to get its markdown version.

# Install the landing-page helper

The helper is one block of JavaScript the advertiser pastes on their own site. When a visitor
arrives from one of your ads, it takes the click id out of the landing URL and stores it on the
advertiser's own domain. Later, when that visitor converts, the page calls
`easq('track', 'first_deposit')` and the conversion is posted to the ad server with the stored
click id attached.

It is the middle option between a tracking pixel and a server-to-server postback: it runs in the
browser like a pixel, but it matches on a first-party value the advertiser stores rather than on a
cookie belonging to your ad-serving domain — so it keeps working where third-party cookies are
blocked. It asks the advertiser for one paste and no backend work.

This page gets the block and installs it. Two more pages cover what comes after:

| Page | What it covers |
|---|---|
| [Landing-page helper reference](https://epom.com/ad-server/docs/guides/conversions/landing-page-helper-reference.md) | Firing a conversion per funnel step, sending a payout, how long the click id lasts, reading the click id from your own code, and every command and option. |
| [Check and troubleshoot the landing-page helper](https://epom.com/ad-server/docs/guides/conversions/landing-page-helper-debug.md) | `easq('debug')`, reading the result of a conversion and the reason it did not count, and what to check when nothing arrives. |

## Who this is for

The advertiser's developer or tag-manager owner, and the account manager walking them through it.
Producing the block takes a campaign and someone in Epom who can open its tracking code — on a
stock network that is the network manager, not the advertiser — see
[Get the pixel and S2S tracking code](https://epom.com/ad-server/docs/guides/conversions/s2s-and-pixels.md). Installing the block needs no backend
work and no Epom account — but verifying it takes both of you. Only the ad server can mint a click
id, so the developer has to be handed a test click URL to try it with, and the commonest failure of
all — nothing stored, reported as `no-clickid` — is fixed on the campaign, not on the page.

## When to use it instead of a pixel or a postback

| | Pixel | Landing-page helper | Server-to-server postback |
|---|---|---|---|
| Where it runs | Advertiser's browser | Advertiser's browser | Advertiser's server |
| What it matches on | A click/view cookie on **your** ad-serving domain | A click id stored on the **advertiser's** domain | A click id the advertiser stored server-side |
| Survives third-party cookie blocking | No | Yes | Yes |
| Survives a different browser or device | No | No | Yes |
| What the advertiser has to do | Paste a pixel on the thank-you page | Paste one block on every page | Store a value, then make an HTTP call |
| Needs the click id in the landing URL | No | **Yes** | Yes |

Pick the helper when the advertiser can edit their pages (or runs a tag manager) but will not build
a backend integration. Pick the postback when the conversion happens off the web — an app install,
a call centre, a payment confirmed days later on another device. The helper can hand the click id
to a backend if you want both: see [Read the click id from your own code](https://epom.com/ad-server/docs/guides/conversions/landing-page-helper-reference.md#read-the-click-id-from-your-own-code).

## Prerequisites

- A conversion action, switched on for the campaign — see
  [Create and edit conversion actions](https://epom.com/ad-server/docs/guides/conversions/conversion-actions.md).
- **The creative's landing URL must carry the click id.** Nothing in the block can create one; it
  can only read one that arrived. Put `$$CLICK_ID$$` into the creative's landing URL:

  ```
  https://yourlanding.com/product?easclid=$$CLICK_ID$$
  ```

  **Add the click id to landing page URLs** on the campaign's **Conversion actions** card does that
  for you, wherever the click-through is built out of the creative's own Link field: **Local File**
  and **External File** images, **Local Video** and **External Video** and the VAST tag creatives
  that carry a `<ClickThrough>`, the email-placement click, and both click-through URLs a
  click-through placement hands out — including the hidden-landing-page one, which resolves the
  link at click time. On **HTML Code**, **Template**, native and OpenRTB creatives the landing page
  lives inside markup somebody else wrote, where the toggle has nothing to rewrite, so there the
  macro has to be written into the landing URL by hand.

  A URL that already carries the macro (or an `easclid=` parameter) is left exactly as written, so
  the two cannot collide. `$$ACTION_DATA$$` is the same token under its older name and still works.
  See [Macros reference](https://epom.com/ad-server/docs/reference/macros.md).
- Access to the advertiser's site — every page, not only the conversion page.

> **Note:** the Snippet tab does not warn you when the landing URL is not carrying a click id. It
> cannot see the creative. The generated block opens with a comment restating the requirement, and
> that comment is the only reminder anyone gets — leave it in place when you hand the block over.

## Steps

### Get the block

1. Go to **Account → Advertisers → advertiser → campaign** and open the **Conversion actions**
   card — its own switch has to be on. On a campaign that already has actions it will be.
2. Select exactly one action row and click **Conversion tracking code**. The grid takes one row at a
   time, so the block you get tracks that one action and carries one `easq('track', …)` line.
3. Pick the **Ad-serving domain** above the tabs — the block is built for that host, and the
   conversion call is sent there. Picking one saves it onto the campaign then and there (it needs
   **Action Tracking: Code** and Update on the campaign), and every tracking code that campaign
   issues from that moment carries the new host: this block, its pixel codes, its postback URLs. It is not a way to look at an alternative host — and a block already
   installed at an advertiser goes on naming the old one. (On the global
   **Account → Conversion Actions** page there is no campaign to save it against, and the choice is
   forgotten when the window closes.)
4. Open the **Snippet** tab and copy the block. The tab is there only for a role with **Action
   Tracking: Server-to-Server**, and only from a campaign — the payout flag baked into the block is a
   campaign fact, so the global **Account → Conversion Actions** page offers the postback tab alone.

The tab's settings — Format, Click ID parameter, event names, payout, and the
**Count one conversion per click** checkbox — are described in
[Get the pixel and S2S tracking code](https://epom.com/ad-server/docs/guides/conversions/s2s-and-pixels.md#the-landing-page-snippet). What comes out
is complete: there is nothing in the block for the integrator to fill in — with one exception, the
`{{…}}` variable names in the optional alternative tag of the Google Tag Manager format, which are
covered under [Install it — Google Tag Manager](#install-it--google-tag-manager).

![The Conversion tracking code window on the Snippet tab: the Ad-serving domain picker above the tab strip, then Format, Click ID parameter, Count one conversion per click, Event name and Payout, and the code box holding the block's opening lines.](https://epom.com/docs-images/ad-server/s2s-and-pixels/tracking-modal-snippet.webp)

The code box scrolls inside itself, so the picture shows only the first lines. Copy the block with
the button in its corner rather than retyping it from the screenshot.

**For a whole funnel in one block,** switch to the **Funnels** tab on the same card, select the
funnel and click **Conversion tracking code**. That block names every step of the funnel in its
`actions` dictionary and carries one `easq('track', …)` line per step, in funnel order — the shape
the rest of this page uses. The tab is not shown until at least one funnel has been saved; see
[Save a conversion funnel and report on it](https://epom.com/ad-server/docs/guides/conversions/funnels.md).

### Read the block

A generated block has three parts. This is the HTML shape; the Google Tag Manager shape carries the
same three parts split across tags. The example is a two-step funnel block — a single-action block
is the same shape with one entry in `actions` and one `track` line.

```html
<!-- The landing URL in the creative must carry the click id:
     https://yourlanding.com/product?easclid=$$CLICK_ID$$ -->
<!-- Epom conversion tracking - on EVERY page, before your own scripts -->
<script>window.easq=window.easq||function(){(easq.q=easq.q||[]).push(arguments)};</script>
<script async src="https://ads.example.com/easq.js"></script>
<script>
easq('config', {
  host: 'https://ads.example.com',
  network: '17',
  payoutOverride: false,
  actions: {
    'registration': ['b21f0c9d4e7a3f5182c6d0ab94e7f331'],
    'first_deposit': ['7c4a1e08b5d29f63a0e1c74b8d2f5069']
  }
});
easq('arbitrate', 'last-click');
</script>

<!-- One line per funnel step, where the step happens -->
<script>easq('track', 'registration');</script>
<script>easq('track', 'first_deposit');</script>
```

1. **The stub** (`window.easq=window.easq||…`) queues calls made before the script has finished
   loading. It must come first.
2. **The script tag** loads `easq.js`. It is `async`, one static file, identical for every network,
   and safe to cache.
3. **The config call** registers the ad-serving host and the event dictionary — and, as a side
   effect, captures the click id from the current URL. That is why the block belongs on **every**
   page: the landing page needs it to store the click, and the conversion page needs it to read the
   click back.

The `easq('track', …)` lines are the conversion. Put each one where that step actually happens, not
where the block sits.

The `easq('arbitrate', 'last-click');` line sits in the same `<script>` as the config call, and it is
there by default: **Count one conversion per click** is ticked when the tab opens, and only
unticking it takes the line out. It settles which of *two of your own ad-serving domains* on one
landing page gets the conversion — the later click wins. Despite the name of the checkbox, it is not
the once-per-click rule described in [Fire a conversion per funnel step](https://epom.com/ad-server/docs/guides/conversions/landing-page-helper-reference.md#fire-a-conversion-per-funnel-step)
and has nothing to do with it. On a page running a single
ad-serving domain it changes nothing.

### Install it — plain HTML

Paste the first three parts into the site template, in `<head>` or at the top of `<body>`, ahead of
your own scripts, so it is present on **every** page. Then move each `easq('track', …)` line to the
page or moment where that step completes — the order-confirmation page, the "account created"
screen, the deposit receipt.

Nothing bad happens when a page carries the block twice. The second copy of the script starts no
second runtime — the file returns immediately when it finds one already loaded — and the second
copy's `config` and `track` calls simply run against the runtime that is there. Calling
`easq('config', …)` more than once for the same host merges rather than duplicating — new event
names are added, so an init block that runs on every page view is fine.

If the site runs a Content-Security-Policy, two origins have to be allowed, and they are not always
the same host: the one named in the `<script src>` belongs in `script-src`, and the `host` from the
config call belongs in `connect-src`, because the conversion is a credentialed `fetch` to it. A
network with a CDN in front of its ad-serving domain generates a block naming the CDN in the script
tag and the serving domain in `host`. That is correct — allow both rather than editing `host` to
match. A third origin comes into it only if your network mirrors conversions to third-party trackers
(see [Piggyback pixels](https://epom.com/ad-server/docs/guides/conversions/piggybacks.md)): those tags come back with the counted conversion and are
written into a hidden same-origin frame, so the advertiser's own policy governs them and a strict
one will block them.

What the block stores, so the advertiser's privacy people can answer for it: two first-party cookies
on the advertiser's own domain — one holding the click ids and which conversions each has already
counted, one small record of which ad-serving host captured last — plus a `localStorage` copy of the
first. The click-id jar and its `localStorage` copy are named after the ad-serving host
(`_easclid_…`); the ownership record is a single `_easq_owner`, shared by every ad-serving host on
the page, which is why it is not namespaced. A counted conversion may also render your network's
configured third-party tags into a hidden frame on the page — the one part of this in which the
advertiser is not the only party, so say so if they ask. All of it is written by the `config` call
as the visitor lands from an ad — at page load, long before any conversion happens. Whether that may run
ahead of the advertiser's own consent tool is the advertiser's decision, not one this page makes for
them — but gate it behind consent and a visitor who lands before granting it arrives with a click id
nothing captures, and their conversion is lost.

### Install it — Google Tag Manager

The Snippet tab's **Google Tag Manager** format splits the same thing into two tags:

- **Tag 1** — Custom HTML, trigger **All Pages**. The stub, the config call and the script tag.
- **Tag 2** — Custom HTML, one tag per funnel step, triggered by the event for that step. It holds
  the single `easq('track', …)` line.

The generated block also offers an alternative to a tag per step: one Tag 2 for everything, with the
event name coming from a GTM variable.

```html
<script>easq('track', '{{Event Name}}');</script>
```

On a campaign that takes a payout from the page, the generated line carries one too:

```html
<script>easq('track', '{{Event Name}}', { payout: {{Order Value}} });</script>
```

`{{Order Value}}` there is a placeholder, and the name is a trap: what belongs in the payout field is
**the price of the conversion**, which is almost never the order total — see
[Send a payout](https://epom.com/ad-server/docs/guides/conversions/landing-page-helper-reference.md#send-a-payout). Point it at the variable that holds the price before you publish the
tag.

Quote a text variable, never a number — a tag manager pastes a variable's *value* straight into the
source, so an unquoted string is a syntax error that kills the whole tag. (A quoted number still
prices correctly; it is simply not the shape the generated block emits.) The generated block already
has the quoting right for your campaign; copy it rather than retyping it.

Tag 2 may safely fire before `easq.js` has arrived: the stub in Tag 1 queues the call, and the
runtime applies every queued `config` before any queued `track`, so a conversion tag that runs ahead
of the script still counts. What it must not do is fire before **Tag 1 itself has executed**. The
stub lives in Tag 1 and Tag 2 is a bare `easq('track', …)` line, so a step tag that runs first throws
`easq is not defined` and the conversion is lost with no diagnostic anywhere. The **All Pages**
trigger normally settles it — Tag 1 runs on the page view, the conversion events come later. Where a
step's trigger is the *same* GTM event as All Pages, though — a Page View trigger scoped to
`/thank-you`, say — nothing orders the two, so give Tag 1 a higher firing priority, or make it the
step tag's setup tag.

Once the block is on every page, fire each step where it happens — see
[Fire a conversion per funnel step](https://epom.com/ad-server/docs/guides/conversions/landing-page-helper-reference.md#fire-a-conversion-per-funnel-step) — and check the
result with `easq('debug')` — see [Check and troubleshoot the landing-page helper](https://epom.com/ad-server/docs/guides/conversions/landing-page-helper-debug.md).

## Corner cases and gotchas

> **Corner case:** the block goes on **every** page. It is not a conversion tag — the config call is
> what captures the click id when the visitor lands, and a copy that appears only on the
> order-confirmation page will store nothing and report `no-clickid` forever.

> **Corner case:** the stored click id is written on the highest domain the browser will accept, so
> a click captured on `lp.shop.example` is readable at `checkout.shop.example`. It cannot cross to a
> different registrable domain. A checkout hosted on a payment provider's own domain is outside the
> advertiser's storage, and the conversion has to be fired from a page back on their site.

> **Corner case:** changing the ad-serving host orphans every click id already stored, and nothing
> avoids it. The storage name is derived from the host, so a block pointed at a new one starts with
> an empty jar and reports `no-clickid` for every visitor who landed before the change — the same
> whether the `host` line was edited by hand in the installed block or the **Ad-serving domain** was
> changed in the tab. What the tab buys is not those click ids: it saves the host onto the campaign,
> so the block and every other code that campaign issues keep naming one domain, where a hand-edited
> `host` leaves the page posting to a domain the campaign never issued a code for. Change the host
> once, and expect a gap the length of the advertiser's conversion lag.

> **Corner case:** the `easq('track', …)` line is safe to fire before the script has loaded. The
> stub queues it, and the runtime applies every queued `config` before any queued `track`. The stub
> has to have run, though: it is the first line of the block, and of Tag 1 in the tag-manager shape.

## Per-role notes

- **Network manager** — produces the block from any campaign they can open; the stock role has the
  **Snippet** tab.
- **Advertiser** — the stock role has no **Snippet** tab and no campaign **Conversion actions**
  card, so it cannot produce the block: ask your network manager for it, or for *Action Tracking:
  Server-to-Server* and *Manage Action Tracking* on your role. See
  [What your network controls](https://epom.com/ad-server/docs/roles/advertiser.md#what-your-network-controls).
- **Publisher** — no **Conversion Actions** section; the block is an advertiser-side artefact.
- **Can view share** — you can open the tracking window and copy the block, but an **Ad-serving
  domain** pick is refused with *Permission denied*.

Exact grants: see [User roles](https://epom.com/ad-server/docs/guides/admin/users-and-roles.md).

## Related

- [Conversion tracking overview](https://epom.com/ad-server/docs/guides/conversions.md)
- [Landing-page helper reference](https://epom.com/ad-server/docs/guides/conversions/landing-page-helper-reference.md)
- [Check and troubleshoot the landing-page helper](https://epom.com/ad-server/docs/guides/conversions/landing-page-helper-debug.md)
- [Get the pixel and S2S tracking code](https://epom.com/ad-server/docs/guides/conversions/s2s-and-pixels.md)
- [Create and edit conversion actions](https://epom.com/ad-server/docs/guides/conversions/conversion-actions.md)
- [Save a conversion funnel and report on it](https://epom.com/ad-server/docs/guides/conversions/funnels.md)
- [Campaigns](https://epom.com/ad-server/docs/guides/advertising/campaigns.md)
- [Macros reference](https://epom.com/ad-server/docs/reference/macros.md)