Deep Links e Deferred Deep Links: Como Taguear a Jornada do Usuário do Web para o App
Do clique na web móvel à primeira abertura no app: Universal Links, Play Install Referrer, UIPasteControl no iOS, Smart Scripts com localStorage e unificação no BigQuery.
Contexto
O usuário clica em um anúncio pago no Instagram ou no Google, navega pela versão mobile do seu e-commerce, encontra uma oferta exclusiva e clica no banner "Baixe o app e ganhe 15% de desconto". Ele é levado à App Store ou Google Play, instala o aplicativo e abre.
O que ele encontra? A tela inicial genérica de boas-vindas, pedindo cadastro ou exibindo banners institucionais. O produto que ele estava olhando sumiu. O cupom evaporou.
Para piorar: no seu painel de atribuição e no GA4, a compra subsequente é classificada como tráfego direto ou orgânico. O time de mídia jura que a campanha de R$ 50 mil converteu; engenharia diz que o SDK está inicializando normalmente; produto reclama que a retenção D1 despencou.
Não se trata de bug no aplicativo. É uma ruptura estrutural entre o protocolo HTTP do navegador e as sandboxes dos sistemas operacionais móveis. Quando o usuário não possui o app instalado e passa pela loja, a sessão web é destruída.
Recuperar esse contexto no primeiro arranque — entregando a tela certa para o usuário e o crédito de conversão para a campanha — é o trabalho do deferred deep linking. Com o fim definitivo dos Firebase Dynamic Links em agosto de 2025, o bloqueio explícito de fingerprinting pela Apple e a blindagem de IP pelo iCloud Private Relay, truques antigos deixaram de funcionar. Este guia detalha a engenharia necessária para implementar essa jornada de ponta a ponta sem perda de dados.
Anatomia do Deep Link: Direto vs. Diferido (Deferred)
Antes de tratar a perda de dados, é preciso separar os três protocolos de abertura e a mecânica do deferimento:
| Mecanismo | Formato | Validação de Domínio | Se o app não estiver instalado | Preserva contexto pós-instalação? |
|---|---|---|---|---|
| Custom URI Scheme | myapp://produto/123 |
Nenhuma (sujeito a colisões e sequestro) | Falha com erro silencioso de URL inválida | Não |
| Universal Links (iOS) | https://empresa.com/... |
Criptográfica via apple-app-site-association |
Fallback para URL web no Safari | Não |
| App Links (Android) | https://empresa.com/... |
Criptográfica via assetlinks.json |
Fallback para URL web no Chrome | Não |
| Deferred Deep Link | Rota HTTPS orquestrada por MMP/serviço | Criptográfica + roteamento em borda | Redirecionamento à loja com retenção de payload | Sim (restaura no 1º arranque) |
Universal Links e App Links funcionam exclusivamente quando o binário nativo já está compilado e presente no dispositivo do usuário. Quando o app não existe, o sistema operacional abre a rota web de contingência.
Se o objetivo de marketing for conduzir o usuário à loja para baixar o app, o redirecionamento tradicional quebra o pipeline. O deferred deep linking adia (defer) a entrega dos parâmetros analíticos e da intenção de navegação até que a instalação termine e a aplicação execute pela primeira vez.
Taxonomia e Preservação de UTMs e Parâmetros
Um erro frequente de tagueamento é esperar que o SDK de atribuição "adivinhe" as origens sem um contrato formal entre a URL da landing page e a chamada da loja.
Em uma jornada multicanal, três classes de parâmetros devem atravessar a ponte:
- Atribuição de Mídia:
utm_source(oupidno MMP),utm_campaign,utm_medium,utm_contenteutm_term. - Identificadores de Clique:
gclid(Google Ads),fbclid(Meta) ettclid(TikTok). - Roteamento Interno e Contexto:
deep_link_value(o identificador de rota ou SKU, ex:produto_4489) e metadados adicionais (ex:cupom=PROMO15).
O gerador de links deve aplicar regras de precedência rígidas (waterfall). Se a URL web contiver parâmetros nativos do MMP e parâmetros padrão da UTM, a biblioteca precisa priorizar a taxonomia da plataforma sem descartar as variáveis universais legíveis para o Data Warehouse:
https://empresa.onelink.me/AbCd?pid=google_ads&c=blackfriday_app&af_sub1=gclid_valor&deep_link_value=sku_4489&deep_link_sub1=PROMO15
Atenção: Parâmetros determinísticos como
gclidefbclidnunca devem ser jogados soltos na raiz se você usa um MMP; mapeie-os explicitamente em campos customizados (comoaf_sub1aaf_sub5no AppsFlyer ou sub-parâmetros no Branch) para garantir que eles apareçam nas tabelas brutas (raw data) e viabilizem retroalimentação via API de conversões.
Android: Pipeline Determinístico via Play Install Referrer API
No ecossistema Android, o deferred deep linking possui suporte nativo do sistema operacional e é 100% determinístico.
Quando o link dinâmico detecta que o dispositivo é Android e não possui o app, ele redireciona para a Google Play Store anexando a query string &referrer=:
https://play.google.com/store/apps/details?id=com.empresa.app&referrer=utm_source%3Dgoogle%26utm_campaign%3Dpromo_app%26gclid%3DAbCd123%26deep_link_value%3Dproduto_4489
Durante o download, a Google Play Store grava essa string codificada em um armazenamento local protegido. No primeiro arranque do app, sua aplicação consulta o serviço local do Google Play via IPC (Inter-Process Communication) através da biblioteca com.android.installreferrer:installreferrer.
Exemplo de implementação em Kotlin:
import android.content.Context
import com.android.installreferrer.api.InstallReferrerClient
import com.android.installreferrer.api.InstallReferrerStateListener
fun extrairContextoInstalacao(context: Context, onDadosProntos: (String) -> Unit) {
val referrerClient = InstallReferrerClient.newBuilder(context).build()
referrerClient.startConnection(object : InstallReferrerStateListener {
override fun onInstallReferrerSetupFinished(responseCode: Int) {
if (responseCode == InstallReferrerClient.InstallReferrerResponse.OK) {
val response = referrerClient.installReferrer
val rawReferrer = response.installReferrer // query string decodificável
// Repassa parâmetros de navegação para o roteador interno e MMP
onDadosProntos(rawReferrer)
referrerClient.endConnection()
}
}
override fun onInstallReferrerServiceDisconnected() {
// Rotina de reconexão se necessário
}
})
}
O registro na Play Store fica disponível por 90 dias e não depende de internet na primeira inicialização para ser lido, pois reside nos serviços locais do dispositivo.
iOS: O Fim do Fingerprinting e a Solução via UIPasteControl
No iOS, a App Store adota uma política de isolamento rigoroso: ela não repassa nenhuma query string de URL para o binário do app.
Historicamente, o mercado contornava isso via fingerprinting probabilístico: no clique na web, o servidor registrava IP, User-Agent, idioma e resolução de tela. No primeiro arranque do app, comparava esses dados.
Esse modelo tornou-se inviável por dois fatores:
- App Tracking Transparency (ATT): A Apple proibiu categoricamente a correlação de dados do dispositivo entre apps e sites de terceiros sem autorização prévia (tracking iOS pós-ATT).
- iCloud Private Relay: Ativado por padrão para assinantes iCloud+, mascara completamente os endereços IP dos usuários no Safari e em WebViews embutidas, destruindo qualquer assertividade estocástica.
A alternativa determinística compatível com as regras de privacidade utiliza a área de transferência local do dispositivo (pasteboard on-device).
No clique na web móvel, uma tela intermediária grava a URL de contexto na área de transferência antes de abrir a App Store. No entanto, desde o iOS 16, a leitura do clipboard por código dispara um modal invasivo: "App deseja colar do Safari. Permitir?".
Para eliminar esse atrito mantendo conformidade com o ATT, deve-se adotar o componente nativo UIPasteControl (iOS 16+) no onboarding ou na primeira tela de transição. Ao tocar no botão nativo, o sistema entende a intenção explícita do usuário e concede acesso sem exibir o alerta de permissão:
import UIKit
import BranchSDK
class OnboardingRoutingViewController: UIViewController, UIPasteConfigurationSupporting {
override func viewDidLoad() {
super.viewDidLoad()
if #available(iOS 16.0, *) {
let config = UIPasteControl.Configuration()
config.baseBackgroundColor = .systemIndigo
config.baseForegroundColor = .white
config.cornerStyle = .capsule
let pasteButton = UIPasteControl(configuration: config)
pasteButton.target = self
pasteButton.frame = CGRect(x: 32, y: 300, width: 260, height: 50)
view.addSubview(pasteButton)
}
}
// Método delegado invocado automaticamente pelo UIPasteControl
func paste(itemProviders: [NSItemProvider]) {
Branch.getInstance().passPaste(itemProviders)
}
}
Com o passPaste(), o SDK de atribuição extrai o payload criptografado da área de transferência local e resolve o deep link diferido de forma determinística, sem depender de IP.
Na Web: Smart Script, LocalStorage e Navegação Multi-Páginas
Um ponto de falha silenciosa ocorre antes mesmo do usuário chegar na loja.
Considere a jornada real:
- O usuário clica em um anúncio com
utm_source=meta&utm_campaign=lancamento&fbclid=123. - Acessa a Landing Page.
- Clica no menu, navega para a categoria "Calçados", depois abre a página do produto específico.
- Na página do produto, três níveis depois, ele clica no Smart Banner: "Abra no App".
Se o seu site só lê parâmetros da URL corrente (window.location.search), os parâmetros de campanha da primeira página já desapareceram há muito tempo. O clique no banner sairá sem UTMs e sem fbclid.
Para blindar o pipeline, o script web deve persistir o payload de aquisição no localStorage na entrada e montar dinamicamente o link do Smart Banner:
(function initWebToAppTracking() {
const params = new URLSearchParams(window.location.search);
// 1. Persistir dados da primeira aterrissagem
if (params.has('utm_source') || params.has('gclid') || params.has('pid')) {
const attributionPayload = {
source: params.get('utm_source') || params.get('pid'),
campaign: params.get('utm_campaign') || params.get('c'),
gclid: params.get('gclid') || '',
fbclid: params.get('fbclid') || '',
capturedAt: Date.now()
};
localStorage.setItem('lead_attribution_payload', JSON.stringify(attributionPayload));
}
// 2. Resgatar payload histórico caso a página atual não possua parâmetros
const saved = JSON.parse(localStorage.getItem('lead_attribution_payload') || '{}');
const currentSku = document.querySelector('meta[name="product-sku"]')?.content || 'home';
// 3. Montar a URL dinâmica do Smart Banner / Botão CTA
const baseLink = "https://empresa.onelink.me/AbCd";
const finalUrl = new URL(baseLink);
finalUrl.searchParams.set('pid', saved.source || 'mobile_web');
finalUrl.searchParams.set('c', saved.campaign || 'organic');
finalUrl.searchParams.set('deep_link_value', currentSku);
if (saved.gclid) finalUrl.searchParams.set('af_sub1', saved.gclid);
if (saved.fbclid) finalUrl.searchParams.set('af_sub2', saved.fbclid);
// 4. Injetar nos botões de incentivo ao download
document.querySelectorAll('.btn-app-download, .smart-banner-link').forEach(el => {
el.href = finalUrl.toString();
});
})();
Dessa forma, não importa quantas páginas o usuário visite: o produto atual vai no deep_link_value e a atribuição da mídia inicial é preservada intacta.
Unificação no Data Warehouse: GA4, Client ID e BigQuery
Mesmo com o MMP atribuindo a instalação, as equipes de Business Intelligence e Engenharia de Dados costumam enfrentar um descompasso no GA4 exportado para o BigQuery: a duplicação de usuários.
No schema do GA4:
- Na Web, o
user_pseudo_idé o Client ID do cookie_ga(ex:123456789.987654321). - No App, o
user_pseudo_idé o Firebase App Instance ID, gerado aleatoriamente na instalação pelo SDK do Firebase.
Se um usuário navega no site mobile, clica no banner, instala o app e conclui uma compra, o BigQuery enxerga dois usuários distintos. A transação in-app fica órfã da sessão de navegação original.
A Solução: Injeção de Identidade Determinística
- Na web móvel, capture o Client ID do GA4 via
gtag('get', ...)e anexe como sub-parâmetro no deferred deep link:deep_link_sub2=CLIENT_ID_WEB. - No app, ao resolver o deferred deep link no primeiro arranque, capture esse valor e envie como propriedade de usuário ou parâmetro de evento no Firebase:
// Exemplo Flutter await FirebaseAnalytics.instance.setUserProperty( name: 'linked_web_client_id', value: payloadFromDeferredLink['deep_link_sub2'], ); - No BigQuery, conecte as duas pontas via SQL:
WITH web_sessions AS (
SELECT
user_pseudo_id AS web_client_id,
traffic_source.source AS source,
traffic_source.name AS campaign,
TIMESTAMP_MICROS(event_timestamp) AS session_time
FROM
`seu-projeto.analytics_123456.events_*`
WHERE
platform = 'WEB'
AND event_name = 'session_start'
AND _TABLE_SUFFIX >= '20260901'
),
app_conversions AS (
SELECT
user_pseudo_id AS app_instance_id,
TIMESTAMP_MICROS(event_timestamp) AS purchase_time,
event_value_in_usd,
(SELECT value.string_value FROM UNNEST(event_params) WHERE key = 'linked_web_client_id') AS linked_web_id
FROM
`seu-projeto.analytics_123456.events_*`
WHERE
platform IN ('ANDROID', 'IOS')
AND event_name = 'in_app_purchase'
AND _TABLE_SUFFIX >= '20260901'
)
SELECT
a.app_instance_id,
w.source,
w.campaign,
a.event_value_in_usd,
w.session_time AS web_touchpoint,
a.purchase_time AS app_conversion_time
FROM
app_conversions a
INNER JOIN
web_sessions w ON a.linked_web_id = w.web_client_id
WHERE
a.purchase_time >= w.session_time;
Essa modelagem fecha o ciclo: a compra no aplicativo é unificada deterministicamente à sessão inicial do navegador móvel, mesmo que o usuário nunca tenha feito login no site.
O Novo Cenário: In-App Browsers e AdAttributionKit (AAK)
Dois fatores de infraestrutura exigem atenção contínua em 2026:
1. In-App Browsers (Instagram, TikTok, Facebook)
Navegadores embutidos em redes sociais bloqueiam a execução de Universal Links para reter o usuário dentro da plataforma. Se o link for clicado dentro do Instagram:
- No Android: Utilize um fallback com esquema sintético
intent://no HTML. O Android força o encerramento da WebView do Instagram e invoca o aplicativo nativo ou a Play Store:<a href="intent://produto/4489#Intent;scheme=https;package=com.empresa.app;S.browser_fallback_url=https%3A%2F%2Fempresa.com%2Fproduto%2F4489;end;"> Continuar no Aplicativo </a> - No iOS: Como o WebKit embutido impede a fuga para esquemas arbitrários, exiba uma página de transição (deepview) instruindo o usuário a tocar no menu
...e selecionar "Abrir no navegador do sistema".
2. Apple AdAttributionKit (AAK) e Web AdAttributionKit (WAAK)
Lançado para suceder o SKAdNetwork e atender às normas do DMA europeu (suporte a marketplaces de aplicativos de terceiros):
- O AAK traz suporte nativo a re-engajamento via Universal Links com o parâmetro
AdAttributionKitReengagementOpen, permitindo postbacks de reativação sem fingerprinting. - O Web AdAttributionKit (WAAK) no Safari padroniza a mensuração de conversões originadas em cliques em sites que resultam em instalações de app via tokens de preservação de privacidade, garantindo conformidade regulatória sem depender de cookies de terceiros.
Checklist de Implementação
- Validação de Domínio: Arquivos
apple-app-site-association(sem extensão, servido via CDN em/.well-known/) eassetlinks.jsondevidamente validados com fingerprint SHA-256 da keystore de produção. - Android Play Install Referrer: Dependência
com.android.installreferrerintegrada no app; rotina IPC capturando a query string na primeira execução antes de chamar o router. - iOS On-Device: Implementação do componente nativo
UIPasteControlno primeiro fluxo do app, evitando requisições probabilísticas bloqueadas pelo ATT e mascaradas pelo Private Relay. - Camada Web: Script capturando parâmetros de UTM,
gclidefbclidno primeiro acesso e persistindo emlocalStoragepara montagem dinâmica de Smart Banners. - Taxonomia de Roteamento: Definição estável do parâmetro
deep_link_valuetratado pelo roteador nativo (React Navigation, GoRouter ou nativo), desacoplado de IDs transitórios de sessão (tagueamento RN e Flutter). - Costura de Identidades: Injeção do GA4 Client ID Web no deep link para consolidação de receita cross-environment no BigQuery.
- Tratamento de IAB: Rotas de fuga configuradas com
intent://(Android) e deepviews informativas (iOS) para tráfego proveniente de redes sociais.
Próximo passo
Garantir que um usuário saia de uma campanha web e termine na tela exata do produto no app recém-instalado não é tarefa de uma única tag no Google Tag Manager — é um contrato de engenharia entre frontend web, mobile e pipeline de dados.
Se a sua empresa já investe em mídia para aquisição de app ou tráfego mobile, mas as instalações continuam caindo em tela inicial genérica e as métricas de conversão não batem entre GA4 e MMP:
Nós auditamos sua arquitetura de tracking, configuramos a passagem de parâmetros via Play Install Referrer e UIPasteControl, e estruturamos os scripts de web-to-app para você parar de desperdiçar orçamento de aquisição.