# Deep Linking

## How mobile deep linking works

There are three distinct scenarios. Getting both right is what “deep linking” actually means in practice.

### Deeplink Ext

Improved customer experience for e-commerce campaigns. When a user clicks on an ad, the user is deeplinked directly into the retailer's app or gracefully fallsback to the app store download page.

#### Bid Request Parameters

The extension deeplink and fallback feature will be sent together where supported by Chartboost SDKs.

| Field  | Type | Description|
|--------|------|------------|
| <span style="font-family:Courier New">imp.ext.deeplink</span>       | Integer | Yes 1 = deeplink supported, 0 = deeplink not supported |
| <span style="font-family:Courier New">imp.ext.fallback</span>       | Integer | Yes 1 = fallback supported, 0 = fallback not supported |

Bid Request Example:

```json
"ext":
{    
  "deeplink": 1,
  "fallback": 1
}
```

#### Bid Response Parameters

The extension deeplink and fallback urls will utilized prioritized above any creative click through upon user interaction with the ad.

| Field  | Type | Description|
|--------|------|------------|
| <span style="font-family:Courier New">seatbid.bid.ext.deeplink</span>       | String | Deeplink url |
| <span style="font-family:Courier New">seatbid.bid.ext.fallback</span>       | String | Fallback url |

**Recommended**
Both deeplink and fallback params are required for Deeplink function as designed. If any of these fields are missing, the Deeplink workflow will attempt deeplink, fallback and creative click throughs in that ordering. For the best user experience, provide both a deeplink and a fallback url.

Bid Response Example:

```json
"seatbid": [
  {
    "bid": [
      {
        "ext": {
          "deeplink": "deeplinkURL",
          "fallback": "fallbackURL"
        }
      }
    ]
  }
]
```

### Direct deep link (app already installed)

The user taps the ad, the app is already on the device, and it opens directly on a specific screen — for example a product page, a promo, or a saved cart — instead of the generic home screen. This is driven by a URI the OS knows how to route to the app, e.g. a custom scheme or a Universal/App Link.

```
myapp://product/12345  
https://www.brand.com/product/12345   (Universal Link / App Link)
```

### Deferred deep link (app NOT installed)

The user taps the ad, does not have the app, is sent to the App Store / Google Play to install, and on first open is routed to the same intended screen. The intended destination has to “survive” the install — the app store strips the original link, so something has to remember where the user was supposed to go. That “something” is the MMP.

### Where the MMP (AppsFlyer) fits in

An MMP like AppsFlyer sits in the middle of the click. Instead of pointing your creative straight at myapp://product/12345, you point it at an **AppsFlyer OneLink / attribution (click) URL**. OneLink is a single link that does three jobs at once:

* **Routing decision** — on tap, AppsFlyer checks the device and app-install status and decides whether to open the app directly, defer through the store, or fall back to a web page.

* **Carries the destination** — the intended in-app screen travels inside the link as parameters (e.g. `af_dp`, `deep_link_value`) and is preserved through the store install so the SDK can deliver it on first open.

* **Attribution** — the same click is recorded for attribution, tying the install and the in-app event back to your campaign, creative and publisher. One integration, one source of truth.

So the rule of thumb for a DSP is: **you pass the AppsFlyer click URL as the click-through, not the raw app URI.** AppsFlyer resolves the rest. The raw URI is embedded **inside** that AppsFlyer link via `af_dp` / `deep_link_value`.

## The AppsFlyer OneLink, decoded

A OneLink click URL has a fixed shape: a OneLink subdomain \+ template ID, followed by query parameters. The parameters fall into three groups — **deep-link destination**, **routing/fallback**, and **attribution/macros**.

```
https://onelink-subdomain.onelink.me/ABcd?
    pid=loopme_int               // media source (partner id)
    &c=summer_sale               // campaign name
    &af_dp=myapp%3A%2F%2Fproduct%2F12345          // deep link URI (installed users)
    &deep_link_value=product_12345                // destination key (deferred / UDL)
    &deep_link_sub1=US&deep_link_sub2=banner      // extra context (optional)
    &af_web_dp=https%3A%2F%2Fbrand.com%2Fp%2F12345 // web fallback
    &clickid=CB-{AUCTION_ID}     // your unique click id (dedupe / postbacks)
    &idfa={IDFA}&advertising_id={GAID}            // device id macros
```

### Deep-link destination parameters

| Parameter | Purpose | Notes |
| :---- | :---- | :---- |
| `af_dp ` | App URI opened for users who ALREADY have the app installed. | Must be URL-encoded. e.g. myapp://product/12345 |
| `deep_link_value` | The destination “key” the app reads via Unified Deep Linking (UDL). Used for both installed and deferred users. | Preferred modern field; app maps it to a screen. |
| `deep_link_sub1–10` | Extra parameters passed alongside the value (e.g. coupon, category, locale). | Optional. Up to 10. |
| `af_web_dp` | Web URL to fall back to if routing to the app/store is not possible. | Recommended for graceful failure. |
| `af_ios_url` / `af_android_url` | Platform-specific web fallback / store override. | Optional, per-OS control. |
| `af_force_deeplink=true` | Force the app to open via the URI even when AppsFlyer would otherwise serve a redirect. | Use with care; test per app. |

### Attribution & macro parameters

These tie the click to your campaign. The Chartboost and LoopMe exchanges substitutes its macros into the URL at serve time; AppsFlyer substitutes its own user-level values. Always supply a unique clickid so clicks can be de-duplicated and matched to postbacks.

| Parameter | Typical value / macro | Purpose |
| :---- | :---- | :---- |
| `pid` | `loopme_int  (fixed)` | Media source — identifies your traffic to AppsFlyer. |
| `c `| `{CAMPAIGN_NAME}` | Campaign name. |
| `af_siteid` | `{APP_BUNDLE}` | Publisher app the impression ran on. |
| `af_c_id` / `af_ad_id` | `{CAMPAIGN_ID}` / `{CREATIVE_ID}` | Campaign / creative IDs for reporting. |
| `clickid` | `CB-{AUCTION_ID}` | Unique click identifier (dedupe \+ postback match). |
| `idfa` / `advertising_id` | `{IDFA}` / `{GAID}` | Device advertising IDs (when available. |

*Note: exact macro tokens (e.g. `{AUCTION_ID}`, `{APP_BUNDLE}`) are defined by the Chartboost exchange spec. Confirm the current token list with your LoopMe/Chartboost integration contact before going live.*

## Passing the deep link through the Chartboost SDK

**Key fact:** the Chartboost OpenRTB 2.6 bid response has **no dedicated deep-link field** (there is no seatbid.bid.ext.deeplink). The deep link is delivered the same way as any click destination — it lives in the **click-through of the ad markup (adm)**. The Chartboost SDK opens whatever click-through URL the creative carries; if that URL is your AppsFlyer OneLink, deep linking and deferred deep linking work automatically.

**Therefore the deep link \= the AppsFlyer click URL placed in the click-through of your **adm**. Build the OneLink, URL-encode it, and drop it into the correct click-through slot for the creative type.

### Where the click-through lives, by creative type

| Creative type (ext.crtype) | Click-through location inside adm |
| :---- | :---- |
| **HTML / MRAID** <br> `"HTML", "MRAID 1.0/2.0/3.0"` | The href of the clickable element, e.g. `<a href="<OneLink>">...</a>` or the MRAID open() call. |
| **MRAID Playable** <br> `"MRAID playable"` | The store/click URL invoked on the end-card CTA (mraid.open with the OneLink). |
| **Video (VAST)** <br> `"VAST 2.0/3.0"` | VideoClicks \> ClickThrough (and CompanionClickThrough on the end card). |

### Example — HTML / MRAID bid response

The OneLink sits in the `<a href>`. Everything else is a standard Chartboost banner/MRAID response.

```
{
  "id": "9d55...", "cur": "USD",
  "seatbid": [{ "seat": "loopme_dsp", "bid": [{
    "id": "rtb-9d55...",
    "impid": "05f23...",            // must match imp.id
    "price": 15.26,
    "adomain": ["brand.com"],
    "bundle": "123456789",          // advertised app store id / package
    "crid": "loopme:dl_12345",
    "mtype": 1,
    "burl": "https://track.loopme.com/imp?ai=${AUCTION_ID}&p=${AUCTION_PRICE}",
    "adm": "<!DOCTYPE html><html><body>
       <a href=\"https://brand.onelink.me/ABcd?pid=loopme_int
          &c=summer_sale&af_dp=myapp%3A%2F%2Fproduct%2F12345
          &deep_link_value=product_12345
          &af_web_dp=https%3A%2F%2Fbrand.com%2Fp%2F12345
          &clickid=CB-${AUCTION_ID}&advertising_id=${GAID}\">
          <img src=\"https://cdn.brand.com/ad.jpg\"/></a>
       </body></html>",
    "ext": { "crtype": "MRAID 2.0",
             "imptrackers": ["https://track.loopme.com/imp?..."] }
  }]}]
}
```

### **4.3 Example — VAST video bid response**

The OneLink goes in `<ClickThrough>` (and, if there is an end card, in `<CompanionClickThrough>`). URL-encode the link inside the CDATA.

```
<VAST version="3.0"><Ad><InLine>
  <AdSystem>LoopMe</AdSystem><AdTitle>summer_sale</AdTitle>
  <Impression><![CDATA[https://track.loopme.com/imp?...]]></Impression>
  <Creatives><Creative><Linear>
    <Duration>00:00:15</Duration>
    <MediaFiles><MediaFile type="video/mp4" delivery="progressive"
       width="568" height="320"><![CDATA[https://cdn/video.mp4]]>
    </MediaFile></MediaFiles>
    <VideoClicks>
      <ClickThrough><![CDATA[https://brand.onelink.me/ABcd?pid=loopme_int
        &c=summer_sale&af_dp=myapp%3A%2F%2Fproduct%2F12345
        &deep_link_value=product_12345
        &af_web_dp=https%3A%2F%2Fbrand.com%2Fp%2F12345
        &clickid=CB-${AUCTION_ID}]]></ClickThrough>
    </VideoClicks>
  </Linear></Creative></Creatives>
</InLine></Ad></VAST>
```

## Step-by-step integration checklist

1. **Get the OneLink template from the advertiser/MMP.** The advertiser sets up the OneLink in AppsFlyer and shares the subdomain \+ template ID and the agreed `pid` for your traffic.

2. **Build the deep-link destination.** Encode the app URI into `af_dp` and set `deep_link_value` (+ any `deep_link_sub*`). Add `af_web_dp` as a web fallback.

3. **Append attribution \+ macros.** Add `pid`, `c`, `clickid`, device-ID and exchange macros.

4. **URL-encode the whole OneLink** before embedding it in the creative (double-check the encoding when it is nested inside JSON or CDATA).

5. **Place it in the click-through** for the creative type: `<a href>` / `mraid.open` for HTML-MRAID, `<ClickThrough>` for VAST.

6. **Set the required bid fields** Chartboost expects: `id`, `impid`, `price`, `adomain`, `crid`, `bundle`, `mtype`, `ext.crtype`, plus `burl` or `ext.imptrackers` for impression tracking.

7. **Test both paths**: app installed \= direct deep link; app not installed \= store then deferred deep link on first open.

## Testing & validation

* **Installed-user (direct) test:** with the advertised app installed, tap the creative in a Chartboost test placement — the app must open on the target screen, not the home screen.

* **Deferred test:** uninstall the app, tap the creative, install from the store, open the app — first launch must land on the same target screen.

* **Attribution check:** confirm the click and install appear in AppsFlyer attributed to `pid=loopme_int` with your clickid and campaign.

* **Encoding check:** decode the click-through URL from the live adm and confirm `af_dp` / `deep_link_value` survived JSON/CDATA nesting intact.

* **Fallback check:** on a device/OS where the app can’t open, confirm the user lands on `af_web_dp` rather than a dead screen.

## Common mistakes to avoid

| Mistake | Why it breaks | Fix |
| :---- | :---- | :---- |
| Passing the raw app URI as the click-through | Deferred users (no app) hit a dead link; no attribution. | Always pass the AppsFlyer OneLink; embed the URI in `af_dp`. |
| Double / missing URL-encoding | Parameters truncate at the first & or get mangled in JSON/CDATA. | Encode the full OneLink once; verify after nesting. |
| Omitting `deep_link_value` | Deferred deep linking can’t resolve the destination via UDL. | Set `deep_link_value` (and `af_dp` for installed users). |
| No `af_web_dp` fallback | Users with no app \+ failed store route hit nothing. | Always include a web fallback URL. |
| Reusing clickid across impressions | Clicks/installs can’t be de-duplicated or matched. | Use a unique id, e.g. `CB-${AUCTION_ID}`. |
| Wrong click-through slot for the format | SDK never fires the deep link. | `<a href>` for HTML/MRAID; `<ClickThrough>` for VAST. |
