Browse the help center

Install the landing-page helper

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.

How-to network manager advertiser Account → Advertisers → advertiser → campaign → Conversion actions → Conversion tracking code → Snippet

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:

PageWhat it covers
Landing-page helper referenceFiring 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 helpereasq('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. 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

PixelLanding-page helperServer-to-server postback
Where it runsAdvertiser’s browserAdvertiser’s browserAdvertiser’s server
What it matches onA click/view cookie on your ad-serving domainA click id stored on the advertiser’s domainA click id the advertiser stored server-side
Survives third-party cookie blockingNoYesYes
Survives a different browser or deviceNoNoYes
What the advertiser has to doPaste a pixel on the thank-you pagePaste one block on every pageStore a value, then make an HTTP call
Needs the click id in the landing URLNoYesYes

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.

Prerequisites

  • A conversion action, switched on for the campaign — see Create and edit conversion actions.

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

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

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.

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.

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.

<!-- 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 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): 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.

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

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

<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. 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 — and check the result with easq('debug') — see Check and troubleshoot the landing-page helper.

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

Related pages

Please disable Adblock
Are you using adblock? Disable it and reload the page to proceed to the destination page.

A registration has already been submitted from this page. Please try again in 10 minutes.