---
title: "Check and troubleshoot the landing-page helper"
description: "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."
type: "howto"
audience: [network-manager, advertiser]
keywords: [landing page helper, easq debug, easq track callback, conversion not counted, reason code, XD-reason, XD-warning, no-clickid, already-tracked, unknown-event, not-configured, lost-last-click, no-campaign-match, not-sent, expired, window-disabled, key-unknown, action-data-missing, memory only, consent, test click URL, nothing arrives]
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.

# Check and troubleshoot the landing-page helper

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](https://epom.com/ad-server/docs/guides/conversions/landing-page-helper.md); counting rules, payout and
click-id lifetime are in the [Landing-page helper reference](https://epom.com/ad-server/docs/guides/conversions/landing-page-helper-reference.md).

## 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](https://epom.com/ad-server/docs/guides/conversions/landing-page-helper.md).
- 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:

```js
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 the `host` line 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 `off` for a generated block; the Snippet tab never turns it on.
- **payout from the page** — `accepted by the campaign`, `IGNORED by the campaign`, or
  `not stated by this snippet` for 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 trip` means 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 `arbitrate` line. 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, `0` on a page that has not
  converted yet and on a network that configured none. See
  [Piggyback pixels](https://epom.com/ad-server/docs/guides/conversions/piggybacks.md).
- 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, `localStorage` unavailable.

`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:

```js
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 `counted` answer 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 as `ok`. 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](https://epom.com/ad-server/docs/guides/conversions/postback-troubleshooting.md#read-why-a-conversion-did-not-count).

> **A refusal releases the step, so it can be retried.** The once-per-click mark is kept only for
> `ok`, `already-tracked`, `expired` and `window-disabled` — the answers that mean the step is
> settled. Every other code releases it, so the same event fires again on the next `easq('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:

1. **Run `easq('debug')` on the landing page, right after clicking a live ad.** If the click id line
   says `NONE`, 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](https://epom.com/ad-server/docs/guides/conversions/landing-page-helper.md#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=test123` is discarded on sight with no message anywhere, so `NONE` after 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.
2. **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.
3. **Check `host` in the debug output** names an ad-serving domain of yours, and the same one the
   ads were served from.
4. **Fire the conversion with a callback** and read the `reason` against the tables above.
5. **Check the event name** in the `track` call against the `actions` list in the block.
6. **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.
7. **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.

## Related

- [Install the landing-page helper](https://epom.com/ad-server/docs/guides/conversions/landing-page-helper.md)
- [Landing-page helper reference](https://epom.com/ad-server/docs/guides/conversions/landing-page-helper-reference.md)
- [Conversion tracking overview](https://epom.com/ad-server/docs/guides/conversions.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)
- [Piggyback pixels](https://epom.com/ad-server/docs/guides/conversions/piggybacks.md)
- [Macros reference](https://epom.com/ad-server/docs/reference/macros.md)