Pular para o conteúdo principal

UTMs

Esta página documenta todos os parâmetros UTM que o front captura, persiste e envia pro BFF — incluindo o wildcard utm_* que permite criar tags custom sem precisar deploy.

Parâmetros canonical​

Os 4 UTMs padrão Google + dois aliases de uso comum:

Campo internoAliases aceitos na URLVai pro BFF como
utm_sourceutm_sourceutm_source
utm_campaignutm_campaignutm_campaign
utm_contentutm_contentutm_content
utm_mediumutm_mediumutm_medium
srcutm_src, srcsrc (apenas em signup, não em deposit)
app_sourceapp_source (+ detecção PWA via matchMedia)app_source

Por que src é separado de utm_source?​

Paridade legado. O Vue antigo usava src para rotular a "fonte composta" (ex: fb_ads, gads_search) enquanto utm_source ficava com o nome canônico do canal (facebook, google). Mantemos os dois pra não quebrar dashboards de BI.

Wildcard utm_*​

QUALQUER query param que comece com utm_ é capturado dinamicamente. Permite marketing adicionar UTMs custom sem deploy:

?utm_oferta=black-friday-50
?utm_term=cassino-online
?utm_creative=banner-vermelho-v3
?utm_placement=feed-mobile
?utm_position=top
?utm_network=display
?utm_target=lookalike-1pct
?utm_match=exact

Todos esses são persistidos no cookie_tracking e enviados flat no payload de signup/deposit (cada um vira top-level key).

Limites do wildcard​

  • Máximo 20 chaves totais no cookie (proteção contra URL stuffing)
  • Canonicals + clicked-IDs têm prioridade — quando cheio, utm_* custom são descartados primeiro
  • Cada valor: max 500 chars, sanitizado (HTML entities, scripts, etc strippados)

Política last-touch (sobrescrita)​

Já detalhado em Atribuição → Overview, em resumo:

  • Cada UTM nova sobrescreve o valor antigo
  • UTMs ausentes na URL nova mantêm o valor antigo (não são apagadas)
  • Vale tanto pra canonicals quanto pra wildcard

:::warning Implicação operacional importante Se a campanha A tem utm_source=facebook&utm_campaign=feed, e o usuário clica depois numa URL com só utm_campaign=cyber-monday, ele fica com utm_source=facebook herdado. Isso pode inflar atribuição do canal anterior.

Solução: sempre passe o set completo de UTMs em cada link de campanha. Use uma ferramenta de UTM builder (Google Analytics URL Builder, Bitly campaigns) que enforce o set completo. :::

TTL configurável por brand​

cookie_tracking tem TTL de 7 dias por default. Brands podem override via featuresConfig.trackingCookieTtlHours:

BrandTTL configuradoOnde
Default base7 dias (168h)app/config/features/features.ts
Vera7 dias (explícito)overrides/vera-bet-br/.../features.ts (linha trackingCookieTtlHours: 168)
7k7 dias (explícito)overrides/7k-bet-br/.../features.ts
Cassino7 dias (explícito)overrides/cassino-bet-br/.../features.ts
Betpontobet10 dias (240h)overrides/betpontobet-bet-br/.../features.ts

Valores possíveis:

  • 24 → 1 dia (atribuição curta, conversão rápida)
  • 168 → 7 dias (default)
  • 720 → 30 dias (atribuição longa, comportamento legado pré-2026)
  • 2160 → 90 dias (janela do cookie do Google Ads)
  • 0.5 → 30 minutos (testes de atribuição extra-curta — pouco usado)

Trocar o TTL encurta ou alonga a janela de atribuição. Em campanhas de venda rápida (BlackFriday flash), TTL curto faz sentido pra evitar atribuir conversão a clique antigo. Em campanhas de awareness (brand campaign), TTL longo faz mais sentido.

Feature flag keepTrackingParamsInUrl​

Controla se os UTMs ficam na URL após captura ou são removidos via redirect 302.

ValorComportamentoImpacto
true (default em todas as brands hoje)UTMs permanecem na URLURL compartilhável carrega atribuição; SEO afetado por params variados; canonical mantém UTMs
falseServer faz 302 pra mesmo path sem UTMs; cookie já foi escrito no redirectURL "limpa"; melhor pra SEO; mas UTMs viram não-compartilháveis

Mudar pra false é uma decisão coordenada — nunca seta no features.ts do base, só em override de brand específica.

Exceção: rota /clever (Clever Advertising affiliate) sempre preserva UTMs na URL, independente da flag, pq o próprio loader da rota lê os params.

Sanitização​

Cada valor passa por sanitizeTrackingValue() (porta direta de getSecureQueryParam do legado Vue):

removidos: < > " ' `
"document" "createElement" "appendChild"
<script>...</script>
<img ... src=...>
descartado: {fbclid} {{campaign.id}} __CLICKID__ __CAMPAIGN_ID__
truncado: valores > 500 chars

Bloqueio absoluto (nunca persistido, mesmo em URLs maliciosas):

KeyPor quê
user_phonePII — TikTok ad macros vazam isso quando mal-configuradas (user_phone=__USER_PHONE__)
user_emailIdem
user_nameIdem
pixelRuído — não é tracking, é ID de pixel da plataforma
currencyRuído — moeda é resolvida pelo BFF, não pela URL

Payload final pro BFF​

Signup (POST /bff/register-simplified ou POST /api/auth/register)​

Body em snake_case com todos os campos flat:

{
"email": "user@example.com",
"password": "...",
"country": "BRA",

"utm_source": "facebook",
"utm_campaign": "black-friday",
"utm_content": "banner-v3",
"utm_medium": "cpc",
"utm_oferta": "first-deposit-100pct",
"utm_term": "cassino",

"src": "fb_ads",
"app_source": "web",
"ga_client_id": "GA1.1.123456789.987654321",
"affiliation_code": "AFIL1",
"subid": "rt_subid_42",

"gclid": "Cj0KCQjw...",
"fbclid": "IwAR3...",
"media_source": "google",
"media_clid": "Cj0KCQjw..."
}

Deposit (POST /wallet/add-credit)​

Body em snake_case, subset diferente do signup:

{
"user_id": 42,
"credit_amount": 5000,
"payment_method": "pix",
"currency": "BRL",

"utm_source": "facebook",
"utm_campaign": "black-friday",
"utm_content": "banner-v3",
"utm_medium": "cpc",
"utm_oferta": "first-deposit-100pct",

"ga_client_id": "GA1.1.123456789.987654321",

"gclid": "Cj0KCQjw...",
"media_source": "google",
"media_clid": "Cj0KCQjw..."
}

:::caution Paridade estrita: src NÃO vai no deposit Nenhum dos 4 projetos legado (base, 7k, cassino, vera) jamais enviou src em /wallet/add-credit — só em /auth/register. Manter essa distinção evita surpresas em dashboards de BI.

affiliation_code, subid, app_source também não vão no deposit. :::

Como testar manualmente​

  1. Limpa cookies do site
  2. Acesse http://localhost:5173/?utm_source=teste&utm_campaign=abc&utm_oferta=novo&ref=AFIL1
  3. DevTools → Application → Cookies → confirme cookie_tracking populado
  4. Clica em outro link com ?utm_campaign=cyber-monday → confirma que utm_source=teste foi preservado e utm_campaign foi sobrescrito
  5. Submete signup → Network → POST /api/auth/register → Form Data deve ter utm_source, utm_campaign, utm_oferta, affiliation_code, app_source
  6. Submete deposit → Network → POST /api/payments/deposit → body deve ter os UTMs sem src/affiliation_code/subid/app_source

Detalhes em Operação → Testar Campanha.

Anti-patterns​

  1. Setar UTM em link interno (ex: header → /?utm_source=header_btn). Sobrescreve a atribuição real e infla "header_btn" como canal. Use data-* ou eventos de UI, nunca UTM em link interno.
  2. Esquecer set completo em link de campanha. Last-touch herda silenciosamente os ausentes.
  3. Pedir captura de campo novo só no front. Se o BFF não processar, é tracking morto. Sempre alinha back primeiro.
  4. Mudar política pra first-touch sem alinhar com BI. Quebra dashboards calibrados.