App Mode (TWA + ?app=true)
A plataforma roda em 3 contextos distintos: web, PWA e App Android (TWA). O modo app tem comportamento de tracking radicalmente diferente do web — esta página explica por quê.
Os 3 contextos
| Contexto | Como detectar | app_source | X-ORIGIN-ACCESS |
|---|---|---|---|
| Web Desktop | User-Agent não-mobile, sem param de app mode | "web" | 2 |
| Web Mobile | User-Agent mobile, sem param de app mode | "web" | 4 |
| PWA Standalone | matchMedia('(display-mode: standalone)') | "pwa" | 2 ou 4 |
| TWA App Android | ?app=true ou ?is_twa=true na URL inicial + bridge window.twa | "app" | 1 ou 5 |
Os dois params que ligam app mode
APP_MODE_QUERY_PARAMS = ["app", "is_twa"] (app/hooks/useAppMode.ts) — qualquer um dos dois
com valor "true" liga o app mode:
| Param | Por que existe |
|---|---|
?app=true | Convenção atual do projeto. |
?is_twa=true | Legado dos APKs 7k/cassino/vera publicados antes do rename. Esses APKs continuam em produção injetando is_twa em cada landing. Enquanto não reconhecíamos o param, isAppMode ficava false pra esses usuários — e como o useAppsFlyer se auto-gateia em isAppMode, os eventos AppsFlyer S2S simplesmente não disparavam pra eles. |
O flag é persistido em sessionStorage["cookie_app_mode"], então sobrevive às navegações SPA
depois da landing inicial.
TWA — Trusted Web Activity
TWA é um APK Android que renderiza o site num WebView Chrome controlado. Comporta-se como app nativo (push, install, full screen, ícone na home), mas o conteúdo é o mesmo HTML/JS/CSS do site.
Identificação no front
URL inicial do TWA carrega ?app=true:
https://7k.bet.br/?app=true
Front lê esse param uma vez no boot e persiste. Subsequentes navegações herdam.
Bridge window.twa
APK injeta uma bridge JS:
window.twa = {
getPostMessageService: () => Promise<{
postMessage: (message: string) => void
}>
}
Front usa essa bridge pra:
- Enviar eventos S2S pro AppsFlyer (via post message → APK encaminha)
- Buscar device IDs (
appsflyer_id,advertising_id,app_version)
Comportamento de tracking em modo app
Pixels de marketing DESATIVADOS
// app/hooks/useAppMode.ts
shouldLoadTrackers: !isAppMode
:::info A cláusula !isBot não existe mais
A fórmula era !isAppMode && !isBot. O ramo de bots saiu junto com o branch de auditoria
sintética na revisão do scheduler (2026-06): bots e Lighthouse hoje carregam os mesmos scripts
que um usuário real — decisão deliberada, pra não inflar score. App mode é o único gate.
:::
Quando isAppMode === true, o useAnalytics não inicializa:
- GTM
- Meta Pixel
- Kwai Pixel
- Taboola
- Microsoft Clarity
- Webtrends Optimize
- Google DV360 Floodlight
Atenção — o gate não cobre tudo. Hotjar, Pendo e o selo RA sobem por outro componente
(AnalyticsLoaders), que não consulta app mode. Eles seguem inertes por config
(enabled: false na maioria das brands), não por causa do modo app.
Razão
App tem requisitos diferentes:
- Atribuição de install é via AppsFlyer (bridge nativa / S2S), não via pixel client
- WebView no APK tem comportamento sutilmente diferente de browser
- Pixels client-side em app inflariam métricas de "web users" com tráfego mobile
O que continua funcionando em app
✅ AppsFlyer — o tracker de marketing ativo em app mode. Duas rotas: bridge nativa
window.twa (dominante em produção) e fallback HTTP. Ver
Plataformas → AppsFlyer.
✅ cookie_tracking — UTMs ainda capturados (deeplink do APK pode trazer UTMs).
✅ cookie_referrer — referrer ainda capturado (do APK).
✅ Cookie de remarketing (<rmkCookie>, ex: rmk7k) — UUID first-party gerado igual web.
✅ Headers X-ORIGIN-* — todos viajam normalmente.
✅ Smartico — gamification continua via SDK (não passa pelo gate de app mode).
Detecção PWA
PWA é diferente de TWA:
- PWA: site instalado via "Add to Home Screen" do browser (Chrome, Safari). Continua sendo browser, com permissões mais robustas.
- TWA: APK próprio na Play Store. Browser é só engine de renderização.
Detection PWA via:
window.matchMedia('(display-mode: standalone)').matches
PWA não desativa pixels client-side (permanece em modo web normal). Apenas marca app_source: "pwa" no cookie_tracking.
Eventos especiais em modo app
?app=true deeplink com UTMs
APK pode lançar deeplink com UTMs:
https://7k.bet.br/?app=true&utm_source=appsflyer&utm_campaign=push_winback
Front captura UTMs normalmente + identifica como app + roteia AppsFlyer S2S em vez de pixel.
App version gating
O limite não é hardcoded — vem da config de AppsFlyer da brand:
// app/utils/metrics/appsflyer-bridge.ts (e useAppsFlyer.ts)
const minVersion = appsFlyerConfig.postMessageMinVersion;
if (!minVersion || !versionGte(appVersion, minVersion)) return;
appVersionvem desessionStorage["cookie_app_version"](populado pelo paramapp_versionda URL que o APK injeta).postMessageMinVersioné per-brand. Hoje só7k-bet-brdefine:"3.0.0".- Sem
postMessageMinVersion, ou com APK abaixo do mínimo, a bridge nativa não é usada — o envio cai no caminho HTTP.
Configuração TWA por brand
Um único brand ainda versiona assetlinks.json neste repo:
| Brand | package_name no assetlinks.json | Path |
|---|---|---|
7k-bet-br | br.bet.k.twa | overrides/7k-bet-br/public/.well-known/assetlinks.json |
| Demais brands do base | (não versionam assetlinks aqui) | — |
:::note Cassino e Vera saíram do base
Os APKs de cassino.bet.br e vera.bet.br continuam existindo, mas as brands foram removidas do
front-web-base em 2026-07 — os assetlinks.json delas não são mais servidos por este repo, fora
do escopo desta doc. (São justamente esses APKs legados que injetam ?is_twa=true.)
:::
Lista viva: ls overrides/*/public/.well-known/assetlinks.json no front-web-base.
assetlinks.json é o Digital Asset Links do Google — prova que o domínio autoriza o APK a operar em modo TWA. Sem ele:
- TWA perde modo url-bar-hidden
- Smart Lock para na 1ª autenticação
- Smartico push subscription quebra
Nunca alterar um caractere desse JSON. package_name + sha256_cert_fingerprints são chaves criptográficas atreladas ao APK no Play Store. Typo quebra TWA em prod até próximo deploy.
Como testar app mode
No browser (sem APK)
- Acesse
http://localhost:<DEV_PORT>/?app=true(ou?is_twa=true) — a porta default é5173, mas pode estar sobrescrita porDEV_PORTno.env.localdo workspace - Console:
// Simula bridge TWAwindow.twa = {getPostMessageService: async () => ({postMessage: (msg) => console.log("TWA bridge msg:", msg)})}
- Submeta deposit → Console deve logar mensagem TWA
- Network → Pixels (Meta, Kwai, etc) NÃO devem disparar
No APK real
Conecta dispositivo Android via USB → Chrome DevTools → chrome://inspect → seleciona WebView do APK.
Anti-patterns
- Disparar pixel client em modo app. Quebra atribuição mobile (apps inflate web metrics).
- Confiar em
_fbpcookie em modo app. Não existe (Meta Pixel não carrega). - Esquecer
?app=true/?is_twa=trueno deeplink que o APK manda. Front roda como web, dispara pixels e o AppsFlyer não dispara. - Modificar
assetlinks.jsonsem coordenar com time mobile. Quebra TWA em prod. - Confundir PWA com TWA. PWA = browser instalado, pixels normais. TWA = APK próprio, pixels de marketing desativados.
- Assumir que bot/Lighthouse não carrega tracker. Carrega — a cláusula
!isBotfoi removida em 2026-06. O único gate é app mode.