WarpLink
Concepts

Deferred Deep Links

How deferred deep links preserve their destination and context through an app install, routing new users to the right place after first launch.

Deferred deep links solve the "click before install" problem. When a user clicks a link but doesn't have the app installed, deferred deep linking preserves the link context through the install flow so the user lands on the right content after installing.

The Problem

Without deferred deep links:

  1. User clicks a product share link
  2. App isn't installed: user goes to the App Store
  3. User installs and opens the app
  4. User lands on the home screen with no context

The link context is lost during the install step.

The Solution

With WarpLink deferred deep links:

  1. Click: User taps a WarpLink URL
  2. Signal capture: WarpLink's redirect page records the click. The server derives the IP and stores it with the normalized language and the timezone
  3. Store redirect: User goes to the App Store or Play Store
  4. Install: User installs the app
  5. First launch detection: The SDK detects first launch and records a completion marker once the check finishes. The marker is gone after an uninstall and never returns from a backup, on iOS and on Android alike, so a reinstall runs the check again
  6. Signal collection: The SDK collects device signals (preferred language, IANA timezone name and offset, platform-specific IDs)
  7. Attribution request: The SDK sends signals to the WarpLink attribution API, which derives the IP from the request
  8. Match: The server matches install-time signals against click-time signals and returns the original link data

The user now opens the app and lands directly on the shared product.

How Matching Works

WarpLink uses a two-tier matching system:

Tier 1: Deterministic

Exact-match signals that guarantee accuracy:

  • iOS (IDFV): Identifier for Vendor, used for re-engagement when the app was previously installed
  • Android (Play Install Referrer): The Play Store passes the click referrer through the install process

Deterministic matches always have confidence 1.0, and only these set matchGuaranteed to true on the result. Gate sensitive work such as automatic sign-in on that flag rather than on a confidence threshold.

Tier 2: Probabilistic (Fingerprint)

When deterministic signals aren't available (first install on iOS, sideloaded Android apps), the server compares browser-side and device-side signals to compute a fingerprint match:

Time Since Clickenriched_tzenrichedbasic
< 1 hour0.850.800.70
< 3 hours0.650.600.50
< 6 hours0.500.450.35
< 24 hours0.300.250.20

Past 24 hours there is no match at all.

enriched_tz is the variant current SDKs send. It hashes the IANA timezone name, for example America/Toronto, which carries far more entropy than the minute offset (roughly 340 zones against 38 offsets) and does not shift at a daylight-saving boundary, so a click and install pair that straddles the change still matches. enriched hashes the offset and is the fallback for SDKs that send no zone name. basic covers signals that carry neither.

Two multipliers then reduce the score. A bucket that held more than one distinct link scores at 0.6 of the band, because the answer was picked from a set rather than found. A click from a carrier-grade NAT or private address also scores at 0.6, a household IPv4 address at 0.9, and an IPv6 address at 1.0.

Confidence decreases over time because IP addresses and network conditions change. If several clicks could match, the most recent claimable one wins and the ambiguity multiplier applies.

Match Window

The match window controls how far back the server looks for matching clicks. It is set per link on the server, in the dashboard, not in the SDK:

  • The default is 6 hours and the ceiling is 24 hours
  • Shorter windows reduce false positives
  • Longer windows catch users who take time to install, but the return is small

The reason the ceiling is low is what the fingerprint key actually identifies. It is a network (IP, language, timezone), not a device, so every phone behind one shared address that also shares a language and a timezone lands in the same bucket. Each extra hour lets another stranger join that bucket while adding almost no real matches, since installs that convert overwhelmingly do so soon after the click.

This governs the probabilistic tier only. The referrer and device ID branches read stored install records instead, so a short window costs them nothing. Links created before the current ceiling may still carry a larger configured value, but the server caps it at 24 hours when it reads the link.

How the deferred payload is stored

When a click is identified as a potential deferred deep link (user redirected to app store), WarpLink temporarily stores the click payload:

The payload is keyed to the device fingerprint and held for the length of the match window, then expires automatically.

Because that key is a network rather than a device, one key can collect clicks from more than one person. WarpLink keeps them as a list, newest first, capped at ten clicks per key. Each match claims one click, so two people behind the same address who tap two different links each get their own. An earlier version replaced the stored click instead of appending, which sent one of those two users to the wrong link.

The attribution API reads this payload when the SDK requests a match.

Caching

The SDK runs the attribution check on first launch and records a durable completion marker once it finishes:

  • First launch: collects signals, sends the attribution request, records the completion marker
  • Subsequent launches: return without another network request
  • An attempt that produced no usable answer (no connectivity, or a match the SDK cannot route to a destination) is not recorded as complete, so the SDK retries the check on the next launch
  • A reinstall is a new install and gets a new check. The completion marker belongs to one install, so deleting the app clears it. A second, device-level marker does outlive the delete, and its only job is to set is_reinstall on the request. See Reinstalls

On this page