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 |
|---|---|---|
| imp.ext.deeplink | Integer | Yes 1 = deeplink supported, 0 = deeplink not supported |
| imp.ext.fallback | Integer | Yes 1 = fallback supported, 0 = fallback not supported |
Bid Request Example:
"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 |
|---|---|---|
| seatbid.bid.ext.deeplink | String | Deeplink url |
| seatbid.bid.ext.fallback | 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:
"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 "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 π
-
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
pidfor your traffic. -
Build the deep-link destination. Encode the app URI into
af_dpand setdeep_link_value(+ anydeep_link_sub*). Addaf_web_dpas a web fallback. -
Append attribution + macros. Add
pid,c,clickid, device-ID and exchange macros. -
URL-encode the whole OneLink before embedding it in the creative (double-check the encoding when it is nested inside JSON or CDATA).
-
Place it in the click-through for the creative type:
<a href>/mraid.openfor HTML-MRAID,<ClickThrough>for VAST. -
Set the required bid fields Chartboost expects:
id,impid,price,adomain,crid,bundle,mtype,ext.crtype, plusburlorext.imptrackersfor impression tracking. -
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_intwith your clickid and campaign. -
Encoding check: decode the click-through URL from the live adm and confirm
af_dp/deep_link_valuesurvived JSON/CDATA nesting intact. -
Fallback check: on a device/OS where the app canβt open, confirm the user lands on
af_web_dprather 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. |