Deep Linking

How mobile deep linking works πŸ”—

There are two distinct scenarios. Getting both right is what β€œdeep linking” actually means in practice.

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)

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.

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
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.

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
"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
"MRAID playable"
The store/click URL invoked on the end-card CTA (mraid.open with the OneLink).
Video (VAST)
"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.