> ## Knowledge Base Index
> Fetch the complete knowledge base index at: https://docs.intractive.app/sitemap.xml
> Use this file to discover available pages before exploring further.
> Pure-Markdown content can be obtained by appending a '.md' suffix to the content URLs listed in the sitemap (without the trailing slash).

# Meta - Why completions get over-counted (and how to fix it)

# Why completions get over-counted (and how to fix it)
Story sessions are saved. When someone reopens a story link, the player restores where they left off — and if they had finished, it reports the story as complete again, immediately, before they touch anything.

Every milestone tag fires on that. So a returning visitor who simply reloads the page books another completion, and another qualified lead, every single time.

This affects Analytics and Meta equally, and it has probably been happening in every story you have ever measured.

| 💡 In one sentence: a resumed session looks identical to a finished run, so completion tags need a gate that only lets them fire on a page where the story actually started.

# What the player actually pushes

The difference is visible in the very first event of the page.
| Situation | First progress event on the page | What fires |
| ---- | ---- | ---- |
| **Genuine run** | `progress = 0`, `depth = 1` | Story start, then halfway, then complete — in order, as the person moves |
| **Resumed, mid-story** | `progress = 47`, `depth = 5` | Anything whose threshold is already passed |
| **Resumed, finished** | `progress = 100`, `depth = last` | Halfway **and** complete **and** any score-based event, all within two seconds of page load |

A genuine run always opens at `progress = 0`. A resumed session never does. That single fact is the whole fix.
# How to spot it in your data
The symptom is a funnel that widens at the bottom. Completions exceeding starts is impossible in reality, so if you see it, you are counting reloads.

|| ⚠️ A real example. A test pixel showed 8 story completions against 4 story starts, with 5 qualified leads. Nobody had completed the story 8 times. Roughly half of those conversions were page reloads of an already-finished session.

Check this before you trust any story funnel:
* Completions should never exceed starts.
* Halfway should never exceed starts.
* If a score-based or qualification event is close to your completion count, be suspicious — both inflate together.

# The fix in Tag Manager
Three pieces: a variable that answers "did this page see a genuine start", a trigger that fires when the answer is no, and that trigger attached as an **exception** on the tags that need protecting.

## 1. The variable
**Variables** → **New** → **Custom JavaScript**. Name it `JS - story started this page`.

```
function() {
  try {
    var dl = window.dataLayer || [];
    for (var i = 0; i < dl.length; i++) {
      var e = dl[i];
      if (e && e.event === 'intractiveStoryProgress' && Number(e.progress) === 0) {
        return 'true';
      }
    }
  } catch (err) {}
  return 'false';
}

```

It scans everything the page has pushed so far and looks for a genuine start. Tag Manager re-evaluates it every time a tag is considered, so it is correct at the moment it matters.

## 2. The blocking trigger
**Triggers** → **New** → **Custom Event**. Name it `Block - resumed session (no story start)`.
* Event name: `.*` with **use regex matching** ticked
* Fire on: **Some Custom Events**
* Condition: `JS - story started this page` **equals** `false`

## 3. Attach it as an exception
Open each milestone tag → **Triggering** → **Add Exception** → choose the blocking trigger.
| Gate these | Why |
| ---- | ---- |
| Halfway / 50% events | Re-fires on any resumed session past the midpoint |
| Story complete events | Re-fires on every reload of a finished story |
| Score or qualification events | Restored variables re-trigger the threshold |
| Answer / variable-updated events | Restored answers are not new answers |
| Leave these alone | Why |
| ---- | ---- |
| Story start / ViewContent | Only fires at `progress = 0`, so it is safe by construction |
| Outbound click / Lead | A click is a real action, whenever it happens |
| Block view | Someone resuming genuinely is viewing that block |
| Config and base pixel tags | Page-level, not milestones |

# The same fix without Tag Manager
If you are running the pixel from **Footer scripts** instead, the gate is one flag rather than three objects:
```
if (Number(o.progress) === 0) { started = true; }
if (!started) return;   // resumed session — send nothing

```

This is already built into the standard snippet. See the article on adding a Meta pixel without Tag Manager.

# Verifying it works
You need a finished session to test against, so do it in this order.
1. Open the story in a **private window** and play it through to the end. Milestone tags should fire normally.
2. **Reload that same finished page.** In Preview, the gated tags should now appear under *Tags not fired*.
3. Reload twice more. Still nothing.

|| ⚠️ Testing a story you have already finished will look broken. The story resumes at the end, the gate suppresses everything, and you see no events. That is the gate working, not a failure. Always start from a private window when you want to see a full funnel.

✅ **Quick checklist**
* A genuine run starts at `progress = 0`; a resumed one never does.
* Gate halfway, complete, qualification and answer events.
* Do not gate start, outbound click, or block view.
* Sanity check forever after: completions must never exceed starts.
* Test from a private window, not from a story you already finished.