Skip to content

What is Deferred Deep Linking?

Deferred deep linking is a technique that preserves a deep link’s destination through the app store install flow. When a user taps a link, does not have the app, installs it, and opens it for the first time, deferred deep linking routes them to the original content instead of the default home screen.

Standard deep links only work when the app is already installed. Without deferred deep linking:

  1. User taps a link to https://myapp.tolinku.com/product/123.
  2. The app is not installed, so the user is redirected to the app store.
  3. The user installs and opens the app.
  4. The app has no memory of the original link. The user lands on the home screen and has to find the product manually.

With deferred deep linking, the original destination is preserved:

  1. User taps a link to https://myapp.tolinku.com/product/123.
  2. The app is not installed. Tolinku stores the link destination and sends the user to the app store.
  3. The user installs and opens the app.
  4. The SDK calls the Tolinku API to claim the deferred link.
  5. The API returns the original path (/product/123).
  6. The app navigates the user directly to the product screen.

The challenge is connecting the link click (step 2) with the app open (step 4) when the user’s identity changes between browser and app. Tolinku uses two matching methods:

On Android, Tolinku appends a referrer token to the Play Store install URL using the referrer query parameter. The referrer string contains a tolk_token parameter with the deferred link token:

https://play.google.com/store/apps/details?id=com.example&referrer=tolk_token%3Dabc123

After the user installs the app, Google’s Install Referrer API makes this referrer string available to the app.

You do not have to read it yourself. The Android, React Native and Flutter SDKs bundle the Install Referrer library and do this inside claimDeferredLink: they read the referrer, pull out the token, claim it, and fall back to signal matching if there was no referrer to read. Nothing extra to install, and the native code is declared Android-only, so an iOS build never compiles it.

This method is deterministic and highly reliable. The referrer survives for days, and it names the exact click rather than inferring it.

Signal-based matching (iOS, web, and Android without a referrer)

Section titled “Signal-based matching (iOS, web, and Android without a referrer)”

iOS has no equivalent of the Install Referrer API, so Tolinku matches the install to the click probabilistically:

  1. On the tap, Tolinku records the device’s address and a set of device signals.
  2. After the install, the SDK reads the same signals and sends them to the API.
  3. The API finds the best match on those signals, preferring the more recent click when two are equally good.

This is a best effort match rather than a guarantee, and it is accurate when the install follows the click closely, which is the normal case.

Both methods sit behind a single call, claimDeferredLink, on every SDK. It tries the referrer where there is one and signals where there is not, so your code does not choose:

const link = await tolinku.deferred.claimDeferredLink({ appspaceId });

Call it once, on the first launch after install.

A claim is consumed the first time it succeeds, and claimDeferredLink remembers that it asked, so calling it again is harmless. Only a settled answer is remembered: “nothing waiting for this device” counts, because no amount of asking will change it, while a dropped request does not, so one bad connection does not spend the install’s only chance at attribution.

The lower-level claimByToken and claimBySignals are still available and unchanged. They ask every time they are called and do no remembering.

Two different limits apply, and they are not the same number:

Claim methodWindow
claimByToken (Android, Play Store referrer)7 days
claimBySignals (iOS, device signals)2 hours

Signal matching is deliberately much shorter because the evidence is weaker. Token matching compares a 96 bit secret, so it is either right or it is not and time does not change that. Signal matching is probabilistic, and its accuracy falls as the pool of candidate clicks grows, so a longer window there would buy more matches at the cost of more wrong ones. A click older than 2 hours is not considered for signal matching, even though the link record itself lives for 7 days.

Signals are gathered by the page Tolinku serves on the click, before the visitor reaches the App Store. This happens on every landing page mode. Routes set to None still collect them, via a small page that reports the signals and immediately replaces itself with the store, so choosing a direct redirect does not cost you deferred linking.

Android does not depend on this at all: it claims by the Play Store referrer token, which survives the install without any device signals.

Signal matching compares five things, one point each:

SignalNotes
Screen sizeWidth and height together, with a 50 pixel tolerance. One point, not two: a device has one screen.
Device pixel ratioThe sharpest signal, since it separates devices that report identical logical dimensions.
TimezoneIANA name, for example Asia/Seoul.
LanguageBCP-47 tag.
OS versionMajor component only, so 17.4 and 17.0 agree.

A signal only counts when both the click and the claim reported it, so an SDK that cannot supply one is not penalised for it. A match needs at least two signals to agree, and those must be more than half of the ones that could be compared, so two agreeing out of four comparable is not enough.

Clicks older than 15 minutes have to agree on everything comparable, not just a majority. A real install lands within a few minutes, so beyond that the chance a still-unclaimed click belongs to this device rather than being a coincidence falls away.

Where several clicks qualify, the one agreeing on the most signals wins, and recency only breaks ties. A more recent click never beats a better-matching one.

Language tags are compared leniently: ko matches ko-KR, because not every platform reports a region. Two tags that both specify a region must agree on it.

Country, and anything else derived from the IP address, is deliberately not a signal. Candidates are already filtered by IP before scoring, so an IP-derived signal would agree for nearly every candidate and hand out a free point, lowering the effective bar rather than raising it.

Country and IP geolocation are not part of the score. The device’s IP address is used to find candidate clicks, not to score them. If the device’s public address changed between the click and the first app open, Tolinku falls back to matching within the same subnet, and in that case requires all four signals to agree rather than two.

  • Referral programs. A referral link carries the referrer’s code through install. The referred user is automatically attributed to the referrer.
  • Personalized onboarding. A campaign link carries context (e.g. a promo code or content preference) that customizes the first-run experience.
  • Content sharing. A user shares an article or product link on social media. New users who install from that link land directly on the shared content.
  • Re-engagement. An email or push notification links to a specific in-app screen. Users who uninstalled and reinstall are taken to the right place.

How Tolinku implements deferred deep linking

Section titled “How Tolinku implements deferred deep linking”

Every Tolinku SDK includes built-in deferred deep link claiming:

  • Android SDKs use Tolinku.deferred.claimByToken(token) with the Play Store referrer.
  • iOS SDKs use Tolinku.deferred.claimBySignals(appspaceId:) with automatic signal collection.
  • Web SDK supports both methods for PWA or web-to-app flows.

The API endpoints used are:

  • GET /api/deferred/claim?token=... (token-based)
  • POST /api/deferred/claim-by-signals (signal-based)
  • POST /api/deferred/signals (update signals before install)

See the Deep Links API reference for full endpoint documentation.

Signal-based claiming requires your Appspace ID. This is not your subdomain, slug, or app name. Passing the wrong value is the most common cause of claimBySignals returning null on every call.

To find it: open the dashboard, go to Settings, and copy the Appspace ID field at the top of the General panel. It looks like 64f0a1b2c3d4e5f60718.