Check and troubleshoot the landing-page helper
Verify an installed landing-page helper with easq('debug'), read the result and the reason code of a conversion, and work through the checks when nothing arrives.
How to see what the landing-page helper actually believes on the advertiser’s page, read what happened to a single conversion, and find out why one did not count. Use it right after installing the block, and whenever a conversion that should have arrived did not. Installing the block is covered in Install the landing-page helper; counting rules, payout and click-id lifetime are in the Landing-page helper reference.
Who this is for
The advertiser’s developer testing the integration, and the account manager on your side who has to
hand them a test click and read the result. Only the ad server can mint a click id, so a check done
by the advertiser alone cannot get past no-clickid.
Prerequisites
- The block installed on every page of the advertiser’s site — see Install the landing-page helper.
- A live click on one of the campaign’s ads, or a test click URL for the campaign from your network contact.
- A browser with developer tools open on the advertiser’s site.
Steps
Check it with easq('debug')
In the browser console on the advertiser’s site, after the script has loaded:
easq('debug')
It prints, and returns, a report of what the runtime actually believes:
easq 1: 1 account(s)
host https://ads.example.com (from config)
storage _easclid_1qk4z9
post-view fallback off
payout from the page IGNORED by the campaign
click id held, 1 cell(s)
actions registration, first_deposit
arbitration last-click, owner _easclid_1qk4z9
third-party tags rendered 0 - none configured for these conversions, or none has been counted yet
account https://ads.example.com (from config), namespace _easclid_1qk4z9
Line by line:
- host — where conversions are sent, and where that host came from.
(from config)is what a generated block always produces.(from script origin)means thehostline was edited out of the block and the runtime is guessing from where the script was loaded. - storage — the name of the cookie holding the click ids, so you can find it in devtools. A
(memory only …)suffix means this click was served without consent (see below). - post-view fallback — always
offfor a generated block; the Snippet tab never turns it on. - payout from the page —
accepted by the campaign,IGNORED by the campaign, ornot stated by this snippetfor a block generated before the flag was recorded. This is the fastest way to settle an argument about a payout that did not apply. It reports what the campaign said when the block was generated, not what it says now — if the flag has been changed since, re-copy the block before trusting this line. - click id — the one line worth reading first.
NONE - if our ads point here, the click id did not survive the tripmeans the landing URL is not carrying the click id, and no amount of work on the conversion page will fix it. - actions — the event names this page will accept.
- arbitration — present only when the block carries the
arbitrateline. It names the mode and the storage namespace of the ad-serving host that captured the last click on this page. - third-party tags rendered — how many times this page has rendered the client-side third-party
tags your network returned with a counted conversion. Always printed,
0on a page that has not converted yet and on a network that configured none. See Piggyback pixels. - Anything after that is a diagnostic collected since the page loaded — a click id recognised under
an unexpected parameter name, a token refused as another network’s,
localStorageunavailable.
easq('debug') is the only command that returns a value, and only once the script has loaded. Two
different things make it return undefined. It was called through the stub — in a <script> tag
written next to the async <script src> — in which case the call is queued and its output prints on
its own once the script arrives, unprompted. Or the script never loaded at all, in which case
nothing prints, ever. The network tab tells them apart.
Read the result of a conversion
Pass a callback to see what happened to a single conversion:
easq('track', 'first_deposit', {
callback: function (result) {
console.log(result.status, result.reason); // e.g. "counted" "ok"
}
});
status is one of three words. counted is the only one that means the conversion was
recorded; rejected and skipped both mean it was not. reason is the value worth acting on.
The callback runs exactly once per easq('track', …) call, after every configured account has
answered, and a callback that throws cannot un-count a conversion that already counted.
Note: a
countedanswer is the ad server’s own word, read off the response. It is not a substitute for the ledger: if something other than the ad server answers the request — a CDN error page in front of a mistyped host, say — the runtime has no reason header to read and treats the response asok. Confirm a new integration in a report, not in the console.
Read why a conversion did not count
Reasons the browser reports without an answer from the ad server:
reason | What happened |
|---|---|
no-clickid | Nothing is stored for this visitor. The landing URL never carried a click id, the visitor arrived some other way, or the storage was cleared. The commonest one by far — check easq('debug') first. |
already-tracked | This action was already counted for this click, in this browser. |
unknown-event | No configured account knows that event name. Compare it with the actions list in the block; case and spaces are forgiven and nothing else is, so an underscore in the key has to be typed. |
not-configured | A track call ran and no config call has been applied — the init block is missing from this page, or it failed to parse. |
lost-last-click | The page arbitrates last-click and another ad-serving domain on it holds the later click. |
no-campaign-match | The event’s key names a campaign and the stored click belongs to a different one. A generated block cannot produce this: the keys the Snippet tab emits name only the action, so they match whichever click is freshest. You will see it only in a block someone hand-wrote around a pixel key (the hash-action-campaign shape from the Pixel Tracking tab). |
not-sent | The request was attempted and failed before any answer came back — the ad-serving host unreachable, the call blocked on the network, the device offline. Nothing was counted and nothing was marked, so the next attempt tries again. |
Reasons that come back from the ad server, on the XD-reason response header:
XD-reason | What happened |
|---|---|
ok | Counted. |
expired | The click matched but fell outside the action’s attribution window. Recorded as a zero-revenue diagnostic, not counted. |
window-disabled | That event type has a zero-length window on this action, so it never converts. |
already-tracked | The ad server’s own duplicate check refused a repeat. |
key-unknown, pair-unknown | Neither the action key in the block nor the ids inside the click id resolve to a conversion action — usually a block copied before the action was changed. |
action-data-missing | The request carried no usable click id — missing or garbled, most often a postback URL truncated somewhere in the chain. |
action-unknown, banner-unknown, campaign-unknown, advertiser-unknown, placement-unknown | The entity behind the click is no longer live in ad serving — deleted, or with conversion tracking switched off. |
country-unknown | The country of the conversion could not be resolved. |
error | The ad server failed to process the conversion. |
The full list, with the other codes the endpoint can return, is in Get the pixel and S2S tracking code.
A refusal releases the step, so it can be retried. The once-per-click mark is kept only for
ok,already-tracked,expiredandwindow-disabled— the answers that mean the step is settled. Every other code releases it, so the same event fires again on the nexteasq('track')call in that browser. That is deliberate: a conversion refused for a reason the page can fix should not be locked out of ever counting. It also means a genuinely broken integration retries on every page view rather than failing once, so treat a reason code that keeps coming back as a fault to fix rather than as noise.
Reading these from the advertiser’s own page works: XD-reason, XD-attribution and XD-warning
are all named in Access-Control-Expose-Headers. In devtools, note that the conversion request
answers 404 when the conversion was not processed and 200 when it was — but the status code
is not the answer. XD-reason is.
A refusal the ad server can be fixed out of — an unknown key, an unknown campaign, a server error — does not consume the once-per-click mark. Correct the configuration and the next fire is attempted again, with no change on the advertiser’s page.
When nothing arrives
Before any of it, make sure the runtime is there at all: if easq('debug') returns undefined and
prints nothing, the script never loaded, and nothing below is readable. Look in the network tab for
the easq.js request — a Content-Security-Policy, an ad blocker or a 404 on the wrong host all end
there.
Then, in order, because the first item is the cause most of the time:
-
Run
easq('debug')on the landing page, right after clicking a live ad. If the click id line saysNONE, stop here — the problem is upstream of the advertiser entirely. Check that the creative’s landing URL carries$$CLICK_ID$$, or that Add the click id to landing page URLs is on for the campaign and the creative is one the toggle can reach (see Prerequisites — on HTML Code, Template, native and OpenRTB creatives it has nothing to rewrite, so there the macro is the only thing to check). Then click a fresh ad: a creative already serving does not pick up the change retroactively.It has to be a real click. Only the ad server can mint a click id, and a hand-typed
?easclid=test123is discarded on sight with no message anywhere, soNONEafter one of those proves nothing. An advertiser’s developer testing on their own cannot produce one — ask your network contact for a test click URL for the campaign. -
Check the block is on the landing page, not only the conversion page. The click is captured by the config call, and a config call that only runs at checkout never sees the landing URL.
-
Check
hostin the debug output names an ad-serving domain of yours, and the same one the ads were served from. -
Fire the conversion with a callback and read the
reasonagainst the tables above. -
Check the event name in the
trackcall against theactionslist in the block. -
If the reason is
already-tracked, you are testing on a click that has already converted. Click a fresh ad — on a different campaign, or from a browser with clean storage — rather than firing again. -
Confirm in a report. The action’s Activity badge turns Live once it has recorded a conversion in the last 2 days; the Conversions Log shows individual rows while the action’s Detailed conversion log option is on.
Allow for the ad servers’ settings cache when you have just changed something on the action or the campaign: those settings reach ad serving on a schedule — every 10 minutes on a standard network — so a test fired straight after a save can still be judged by the previous values.
Corner cases and gotchas
Corner case: when the ad was served without consent, the click id is held in the page’s memory and never written to the visitor’s device. A conversion completed in the same document — a single-page checkout — still counts, because the click id in the URL names the click on its own. A real navigation loses it.
easq('debug')marks such an account(memory only - consent was denied for this click).
Per-role notes
- Network manager — the person the advertiser’s developer asks for a test click URL, and the one who confirms the conversion on the action’s Activity badge.
- Advertiser — can run every console check on their own site. The stock role has no campaign Conversion actions card, so the Activity badge is not in reach: ask your network manager to confirm the conversion.
- Publisher — not involved; the block is an advertiser-side artefact.