Web-to-App Cross-Device Attribution

Ad clicks happen in a browser, but the conversions that matter often happen inside a native app, sometimes days later and usually on a different device.

Every ad platform’s default tracking assumes the click and the conversion share a cookie or a device ID, and the moment a user crosses from web to app that assumption breaks. Web pixels lose the user when they leave the browser. Mobile SDKs and SKAdNetwork only know about app-install campaigns, not a web click that turned into an app purchase a week later. The result is a funnel where the top is measured in one system, the bottom in another, and the platforms optimizing your spend never see the connection.

The fix is not a tracking tool. It is a first-party identity created at signup. Capture the ad click IDs on the landing page, store them against the user record the moment an account is created, and every downstream conversion can be sent server-side with the original click attached. This post walks through how that works across real funnel shapes.

Key Concepts

A few terms carry the whole approach.

Ad click IDs. Every major platform appends a unique identifier to the landing page URL when someone clicks an ad. Meta uses fbclid, which the Meta Pixel stores in the _fbc cookie in the format fb.1.{timestamp}.{fbclid}. Google uses gclid. Reddit uses rdt_cid. TikTok uses ttclid. These are the platforms’ own proof that a click happened, and they are the only thing that lets a server-side event be tied back to a specific ad.

First-party identity. The user_id your system generates at account creation. Sent to the ad platforms as external_id, it becomes a match key that works on any device because it lives in your database, not in a cookie.

Server-side conversion APIs. Meta’s Conversions API, Google Ads offline conversion imports, Reddit’s Conversions API, and TikTok’s Events API all accept conversion events from your backend. Each one requires at least one match key, and each one attributes the event to a click when the click ID is present and inside the attribution window.

Attribution windows. Every platform stops crediting a click after a set number of days. The window is measured from click to conversion, not from conversion to upload. This matters for long sales cycles and is covered in its own section below.

Passwordless verification. A 6-digit code sent to an email address or phone number. The user proves ownership of a contact identifier instead of remembering a password, which means the identifier itself becomes the anchor that stitches web and app sessions together.

Web Ad Clicks to On-Demand In-App Purchases

This is the most complete case: a B2B product with Android and iOS apps where the money changes hands inside the app, but the ads point to web landing pages.

Web Landing Page to In-App Purchase

  1. Ad click. The user clicks an ad on Meta, Google, Reddit, or TikTok. The platform appends its click ID to the landing page URL.
  2. Landing page. A small script reads the click ID parameters from the URL and writes them to first-party cookies. The Meta Pixel handles _fbc on its own if it is installed; the others need a few lines of JavaScript.
  3. Sign-up. The user enters an email address or phone number.
  4. 6-digit code. A verification code is sent to that address or number. The user enters it.
  5. Account created. The backend generates a user_id. At this exact moment the click IDs from the cookies are written to the user record along with a timestamp. This is the step everything else depends on.
  6. Deep link or auto-redirect. The web app detects the platform from the user agent and either shows a deep link to the App Store or Play Store or redirects automatically.
  7. Log in. In the mobile app the user enters the same email or phone and receives the same style of 6-digit code. The app resolves to the same user_id.
  8. Billing info. Payment method is added in-app.
  9. On-demand purchase. The conversion event. This fires from the backend, not the app SDK, with the stored click IDs attached.

App Store Install to In-App Purchase

Plenty of users never touch the landing page. They search the App Store or Play Store directly, or they clicked an ad weeks ago, browsed the landing page, and left without signing up.

  1. The user installs the app from the store.
  2. The app prompts for account creation with phone or email options.
  3. The user enters the 6-digit code, which either creates a new account or logs into an existing one.
  4. Billing info is entered and one-time or in-app purchases are made.

If the email or phone the user verifies in the app was already seen on a landing page visit where click IDs were captured but no account was created, those click IDs can still be attached at the moment the account resolves. The landing page just needs to store the contact identifier alongside the click IDs before the user abandons. If there was no prior web visit, the conversion is sent with external_id and hashed contact info only, and the platforms attribute it through their own identity graphs where they can.

How Passwordless Auth Solves Cross-Device Attribution

The verified email or phone is the same identifier on every surface. A user who signs up on a laptop and logs into the app on a phone resolves to one user_id because both sessions proved ownership of the same contact identifier. There is no cookie to lose, no device ID to bridge, and no probabilistic fingerprinting.

This also means the app store path and the web path converge. Both end at the same user record, and the user record is where the click IDs live.

Preserving Ad Click IDs Across Devices and Platforms

The mechanics:

  • Capture fbclid, gclid, rdt_cid, and ttclid on the landing page and store them in first-party cookies. Also capture _fbp (Meta’s browser ID) and _rdt_uuid (Reddit’s) since both improve match quality.
  • At account creation, write all of them to the user record with a click_captured_at timestamp.
  • When a purchase happens, the backend looks up the user, pulls the stored click IDs, and sends one event to each platform’s conversions API.

Per platform, the match keys that matter:

  • Meta Conversions API: fbc, fbp, external_id, hashed em and ph, client_ip_address, client_user_agent. Set action_source to app for in-app purchases. Only matched events count toward attribution and optimization, so send every key you have.
  • Google Ads: gclid plus conversion name, conversion time (after the click time), and value. Enhanced conversions add hashed email and phone as fallbacks.
  • Reddit Conversions API: click_id from rdt_cid, plus email, external_id, and uuid. Reddit requires at least one attribution signal per event, and without the click ID there is no click-based attribution at all.
  • TikTok Events API: ttclid plus hashed email, phone, and external_id.

The user experience side of this is what makes it hold up. The user never re-enters anything. The deep link or auto-redirect carries them to the app, the 6-digit code logs them in with the identifier they already verified, and the click IDs ride along on the user record without any client-side handoff.

Two more details keep the data clean:

  • Deduplication. If the web pixel also fires a purchase or signup event, send the same event_id on the browser event and the server event so the platform discards the duplicate.
  • Timestamps. Store when the click was captured. The platforms reject conversions with a conversion time earlier than the click time, and the stored timestamp is how you know whether a conversion still falls inside the attribution window.

Web Ad Clicks to Mobile App Signups

The second case is simpler. There are no in-app purchases to track. Account creation is the conversion, and it happens on the web.

This is how it works for cred.ai. All ads point to cred.ai landing pages. Signup is handled on apply.cred.ai after the ad click, and the account created there is the same account the user logs into inside the iOS and Android apps.

  1. Ad click. Click ID appended to the landing page URL on cred.ai.
  2. Landing page. Click IDs stored in first-party cookies.
  3. Signup form. The user moves to apply.cred.ai and completes the application. Cookies scoped to the root domain carry the click IDs across the subdomain.
  4. Account created. The conversion event. user_id is generated, click IDs are written to the user record, and the signup event is sent server-side to each platform with external_id, the click IDs, and hashed contact info.
  5. Mobile app. The user installs the app and logs in with the same account. Any downstream activity, from app install to activation to a funded account, can be sent as additional server-side events tied to the same user_id and the same stored click IDs.

The pattern is identical to the purchase case. The only difference is which event the ad platforms optimize toward.

Extending Attribution Windows with Offline Conversions

Standard web and app conversion events are limited to short click windows. Meta’s default is 7-day click. Long B2B sales cycles routinely convert outside that, which means the purchase never gets credited to the ad no matter how well the click ID was preserved.

Offline conversion uploads extend the window on some platforms, but they never remove it. The limit is always click-to-conversion time, not upload time.

Platform Limits

  • Google Ads. The click-through conversion window on an offline import conversion action can be set anywhere from 1 to 90 days. Google retains the gclid for 90 days. After that the click cannot be attributed regardless of settings.
  • Meta. Offline conversions get up to a 28-day click window, versus 7-day click for standard web and app events. Upload within 62 days of the conversion.
  • Reddit. No published extended offline window. Treat it as the standard click window.

How to Send Them

The stored click ID and external_id on the user record are the same inputs.

  • Google: offline conversion import with gclid, a conversion_date_time after the click time, and the extended window set on the conversion action.
  • Meta: Conversions API offline events sent to a dataset, with action_source set appropriately, plus hashed email and phone and external_id as match keys.
  • Deduplicate with order_id or event_id so re-uploads do not double count.

Strategies for Long Sales Cycles

  • Send an earlier funnel event as the optimization conversion. Signup, verified account, trial start, or billing info added all land inside the window. Send the purchase later as a secondary event for reporting.
  • Use Google’s 90-day window for the purchase event specifically, since it is the longest available.
  • For anything past the platform windows, attribute in your own warehouse from the stored click ID and report on it yourself. The platforms will not count it, but the data is still yours.

Closing

The pattern is the same regardless of funnel shape. Capture click IDs on the web. Anchor them to a verified identity at signup. Fire conversions server-side with the stored IDs. Passwordless verification makes the identity portable across devices, and the user record makes the click IDs portable with it.

What this does not solve: users who never sign up, and clicks that fall outside the attribution window. Both are limits of the platforms, not the approach. Building this kind of measurement is the core of my analytics and conversion tracking consulting, usually alongside the server-side event work it depends on.

Sources