> ## Documentation Index
> Fetch the complete documentation index at: https://developers.everflow.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Advanced Patterns

> Multi-call SDK patterns: same-page click + conversion, multi-account chaining, and global-script deduplication.

These patterns combine multiple `EF.click()` / `EF.conversion()` calls or add custom logic around them. They build on the basics in [Click Tracking](/sdk/click-tracking) and the [Tracking Recipes](/sdk/recipes).

## Click and conversion on the same page

When a single page load needs to record **both** a click and a conversion — for example a Meta "PageView" that should attribute the click and immediately convert — fire the conversion inside the click's `.then()`, so it runs only after the click resolves with a transaction ID. A short `setTimeout` adds a safety margin.

```html theme={null}
<script type="text/javascript"
    src="https://INSERT_TRACKING_DOMAIN/scripts/main.js"></script>

<script type="text/javascript">
EF.click({
    offer_id: EF.urlParameter('oid'),
    affiliate_id: EF.urlParameter('affid'),
    sub1: EF.urlParameter('sub1'),
    sub2: EF.urlParameter('sub2'),
    sub3: EF.urlParameter('sub3'),
    sub4: EF.urlParameter('sub4'),
    sub5: EF.urlParameter('sub5'),
    uid: EF.urlParameter('uid'),
    source_id: EF.urlParameter('source_id'),
    transaction_id: EF.urlParameter('_ef_transaction_id'),
}).then(function(transactionId) {
    setTimeout(function() {
        EF.conversion({
            aid: INSERT_ADVERTISER_ID,
            adv_event_id: INSERT_ADVERTISER_EVENT_ID,
            transaction_id: transactionId,
        });
    }, 500);
});
</script>
```

<Note>
  Chaining the conversion inside `.then()` already guarantees it runs after the click is recorded (the promise resolves with the transaction ID). The `setTimeout` is an extra safeguard so the conversion never races ahead of the click on slow connections.
</Note>

## Chaining across multiple Everflow accounts

This extends the [EF-to-EF recipe](/sdk/recipes#ef-to-ef) to more than two accounts. Each hop fires a click against its own `tracking_domain`; the transaction ID returned by one hop is passed into the **next** hop's `sub5` (or `sub2`), which the downstream account reads back via a **Partner Postback**.

### Three accounts deep (Advertiser → Partner → Sub-Partner)

The chain fires inside-out: the sub-partner click resolves first, its transaction ID becomes the partner click's `sub2`, and the partner's transaction ID becomes the advertiser click's `sub5`.

```html theme={null}
<script type="text/javascript"
    src="https://INSERT_ADVERTISER_DOMAIN/scripts/main.js"></script>

<script type="text/javascript">
if (EF.urlParameter('affid3')) {
  EF.click({
    tracking_domain: "https://INSERT_SUB_PARTNER_DOMAIN",
    offer_id: EF.urlParameter('oid3'),
    affiliate_id: EF.urlParameter('affid3'),
  }).then(function(transaction_id) {
    EF.click({
      tracking_domain: "https://INSERT_PARTNER_DOMAIN",
      offer_id: EF.urlParameter('oid2'),
      affiliate_id: EF.urlParameter('affid2'),
      sub2: transaction_id,
    }).then(function(transaction_id2) {
      EF.click({
        tracking_domain: "https://INSERT_ADVERTISER_DOMAIN",
        offer_id: EF.urlParameter('oid'),
        affiliate_id: EF.urlParameter('affid'),
        sub5: transaction_id2,
      });
    });
  });
} else if (EF.urlParameter('affid2')) {
  // Two-account chain (Advertiser → Partner)
  EF.click({
    tracking_domain: "https://INSERT_PARTNER_DOMAIN",
    offer_id: EF.urlParameter('oid2'),
    affiliate_id: EF.urlParameter('affid2'),
  }).then(function(transaction_id) {
    EF.click({
      tracking_domain: "https://INSERT_ADVERTISER_DOMAIN",
      offer_id: EF.urlParameter('oid'),
      affiliate_id: EF.urlParameter('affid'),
      sub5: transaction_id,
    });
  });
} else {
  // Direct advertiser click
  EF.click({
    tracking_domain: "https://INSERT_ADVERTISER_DOMAIN",
    offer_id: EF.urlParameter('oid'),
    affiliate_id: EF.urlParameter('affid'),
    transaction_id: EF.urlParameter('_ef_transaction_id'),
  });
}
</script>
```

Each downstream account recovers the upstream transaction ID with a Partner Postback macro:

* **Partner Postback** (advertiser account): `&transaction_id={sub5}`
* **Sub-Partner Postback** (partner account): `&transaction_id={sub2}`

<Note>
  Each hop reserves a sub-placement (`sub5`, then `sub2`) to carry the upstream transaction ID. Plan your sub usage so the chain doesn't overwrite a sub you need for reporting.
</Note>

### Parallel partners

When one advertiser works with **several independent partners** on the same page (Partner A via `affid2`, Partner B via `affid3`) rather than a nested chain, the structure is the same `if / else if / else` shape — each branch fires the matching partner's click, then chains the advertiser click with the partner's transaction ID in `sub5`. Use whichever partner parameter (`affid2`, `affid3`, …) is present on the inbound URL to select the branch.

## Preventing duplicate clicks with a global script

When the click script is placed **globally** (on every page) and the landing page's URL parameters are "sticky" (persist across navigation), the same partner's click can fire more than once — so the last-touch transaction may not be the one that converts. Deduplicate by comparing the inbound `affid` against the `ef_affid` first-party cookie the SDK writes on every successful click, and only fire when they differ.

**Logic:**

* **Direct linking** — if `affid` (URL) ≠ `ef_affid` (cookie) → fire the click; if they match → skip it.
* **Redirect** — always fire.

```html theme={null}
<script type="text/javascript"
    src="https://INSERT_TRACKING_DOMAIN/scripts/main.js"></script>

<script type="text/javascript">
function getCookie(name) {
    var decodedCookie = decodeURIComponent(document.cookie);
    var cookieArray = decodedCookie.split('; ');
    var prefix = name + "=";
    for (var i = 0; i < cookieArray.length; i++) {
        var cookie = cookieArray[i];
        while (cookie.charAt(0) === ' ') {
            cookie = cookie.substring(1);
        }
        if (cookie.indexOf(prefix) === 0) {
            return cookie.substring(prefix.length);
        }
    }
    return null;
}

if (EF.urlParameter('affid')) {
    var aff_cookie = getCookie('ef_affid');
    var param = EF.urlParameter('affid');
    if (aff_cookie != param) {
        EF.click({
            offer_id: EF.urlParameter('oid'),
            affiliate_id: EF.urlParameter('affid'),
            sub1: EF.urlParameter('sub1'),
            sub2: EF.urlParameter('sub2'),
            sub3: EF.urlParameter('sub3'),
            sub4: EF.urlParameter('sub4'),
            sub5: EF.urlParameter('sub5'),
            uid: EF.urlParameter('uid'),
            source_id: EF.urlParameter('source_id'),
            transaction_id: EF.urlParameter('_ef_transaction_id'),
        });
    }
} else {
    EF.click({
        offer_id: EF.urlParameter('oid'),
        affiliate_id: EF.urlParameter('affid'),
        sub1: EF.urlParameter('sub1'),
        sub2: EF.urlParameter('sub2'),
        sub3: EF.urlParameter('sub3'),
        sub4: EF.urlParameter('sub4'),
        sub5: EF.urlParameter('sub5'),
        uid: EF.urlParameter('uid'),
        source_id: EF.urlParameter('source_id'),
        transaction_id: EF.urlParameter('_ef_transaction_id'),
    });
}
</script>
```

<Note>
  This prevents duplicate clicks **within the same partner**. If Partner A's link is clicked and then Partner B's, Partner B still gets a fresh transaction ID — last-touch attribution is preserved across different partners.
</Note>
