Resumo: A Atribuição Baseada em Pessoas (PBA) está sendo substituída pela mensuração da performance web: uma solução atualizada da AppsFlyer para a mensuração web. Este artigo inclui as mudanças, as etapas de migração e o mapeamento completo dos campos para seus relatórios e integrações do lado do servidor.
Por que estamos atualizando nossa mensuração web
A mensuração web tornou-se mais importante do que nunca. É no seu site que muitos usuários convertem e iniciam a jornada até o seu aplicativo. Mais do apenas uma landing page, seu site é um funil completo de aquisição, incluindo questionários interativos, onboarding personalizado, paywalls e lojas online, que muitas vezes atraem usuários com alta intenção a um custo menor.
A AppsFlyer está elevando a mensuração na web ao mesmo nível da mensuração mobile. A Atribuição Baseada em Pessoas (PBA), nossa solução web legada, está sendo substituída pela mensuração da performance web, desenvolvida a partir do mecanismo central de atribuição e do modelo de dados unificados da AppsFlyer. Nossa nova solução oferece os mesmos recursos da PBA, além de:
- Uma única fonte confiável: mensuração de aplicativos web e mobile em um só lugar, reunindo agregação de custos, postbacks de otimização e otimização de criativos.
- Decisões de orçamento mais eficientes: atribuição flexível e alinhada à lógica do seu negócio, para que você invista mais nas campanhas que geram resultados reais.
- Melhoria no ROAS: postbacks de otimização enriquecidos para suas ad networks e uma configuração completa do lado do servidor que mensura mais conversões, permitindo que as redes otimizem com sinais mais eficazes.
- ROI cross-platform real: mensuração das jornadas de usuários na web, nos apps e em qualquer plataforma.
- Relatórios simples e unificados: um dashboard e um conjunto de relatórios de dados para cada plataforma, atualizados a cada hora.
Atenção
A mudança exige um esforço mínimo. Você pode manter o mesmo Web SDK, sem alterar o código do seu site.
Importante!
Pode haver algumas diferenças nos números. A lógica de atribuição na mensuração da performance web é mais avançada e flexível, podendo apresentar números diferentes dos da PBA.
Mensuração da performance web vs. PBA
| Recursos | Descrição | PBA | Mensuração da performance web |
|---|---|---|---|
| Dados | |||
| Atualização de dados | Com que rapidez os dados estão disponíveis para relatórios? | ✗ Diariamente | ✓ Relatórios por hora, com atualização de dados em aproximadamente 2 horas |
| Modelo de dados | Alinhamento de esquemas com dados de atribuição mobile | ✗ Diferente do mobile | ✓ Alinhamento com o mobile, fácil de integrar e analisar |
| Relatórios | |||
| Dashboard de atividade | Dashboard baseado no horário do evento | ✓ Disponível | ✓ Disponível |
| Dashboard de cohort | Análise de performance por cohort de aquisição ao longo do tempo | ✗ Indisponível | ✓ Disponível |
| Dados brutos do Data Locker | Acesso a dados brutos de atribuição através da exportação do Data Locker | ✓ Disponível | ✓ Disponível |
| Granularidade no nível do anúncio | Análise de relatório desde a campanha até o anúncio | ✗ Apenas no dashboard, não nos dados brutos | ✓ Granularidade completa da campanha tanto no dashboard como nos dados brutos |
| LTV cross-platform | Jornadas de usuários web e mobile conectadas para um LTV unificado | ✗ Indisponível | ✓ Disponível |
| Custo e sinais | |||
| Custo da web | Integração e relatórios dos gastos com anúncios de campanhas na web | ✗ Indisponível | ✓ Disponível |
| Postbacks de otimização | Retorno de sinais de otimização para ad networks | ✗ Indisponível | ✓ Disponível |
| Implementação | |||
| Web SDK (Pixel) | SDK do lado do usuário para mensurar visitas e eventos na web | ✓ Disponível | ✓ Mesmo SDK, sem alterações de código necessárias |
| Suporte a S2S | Mensuração completa do lado do servidor para visitas e eventos | ✗ Apenas eventos | ✓ Eventos e visitas. Oferece uma implementação completa do lado do servidor. |
| Mecanismo de atribuição | |||
| Janelas de atribuição | Janelas configuráveis de lookback, atribuição, reengajamento e inatividade | ✗ Apenas janelas de lookback de clique | ✓ 5 janelas configuráveis (controle total) |
| Eventos de UA personalizados | Evento considerado como aquisição do usuário além da primeira visita | ✗ Apenas a primeira visita | ✓ Qualquer evento personalizado (por exemplo, assinatura e compra) |
| UA vs. retargeting | Créditos separados para nova aquisição vs. reengajamento | ✗ Sem conceito de UA e eventos atribuídos ao último toque | ✓ Visões específicas de UA e retargeting com créditos de atribuição para ambos |
Etapas da migração
A tabela abaixo divide a migração em cinco etapas. Todos precisam criar um aplicativo web; o restante depende dos recursos da PBA que você usa atualmente, como a API S2S ou os relatórios do Data Locker.
| Ação | Relevante para | Como funciona |
|---|---|---|
| Criar um aplicativo web | Todos | Ao criar um aplicativo web, selecione sua web dev key no campo Web SDK ID. O código atual do seu site continua funcionando normalmente, sem precisar de nenhuma alteração. Depois defina as configurações de atribuição no novo aplicativo. |
| Migrar para a nova API S2S | Uso da API S2S da PBA | Configure a nova API S2S. Ela é compatível com visitas e eventos, permitindo que você opere inteiramente no lado do servidor. Veja o apêndice sobre a migração da API S2S abaixo. |
| Migrar para os novos relatórios do Data Locker | Acesso a relatórios de PBA do Data Locker | Ative o novo relatório web na interface e direcione sua ferramenta de BI/ETL para os dados atualizados. Os novos relatórios seguem um esquema compartilhado por todas as plataformas, aplicativos mobile, sites, CTV e PC. Veja o apêndice sobre o mapeamento dos campos de dados brutos abaixo. |
| Analisar os parâmetros de atribuição | Recomendado | A PBA aplicou as próprias regras de atribuição de fontes de mídia. A mensuração da performance web usa a identificação de fontes de tráfego para aprimorar as análises. Analise as diferenças para saber quais valores esperar nos seus relatórios. Veja o apêndice sobre a comparação entre a identificação de fontes de tráfego abaixo. |
| Agrupar aplicativos em uma linha de produtos | Opcional | Crie uma linha de produtos e agrupe o aplicativo web com seus apps mobile para acessar os relatórios de LTV dos usuários entre plataformas (semelhante ao Brand Bundle da PBA). |
Atenção
Pode haver algumas diferenças nos números. A lógica de atribuição na mensuração da performance web é mais avançada e flexível, podendo apresentar números diferentes dos da PBA. A lógica de gravação de sessões permanece inalterada; uma sessão continua ativa por 30 minutos (padrão).
Recursos que serão descontinuados
| Recursos | Mudanças | Detalhes |
|---|---|---|
| Caminho de conversão | Já foi desativado. | — |
| Instalações assistidas pela web (crédito a uma campanha na web que ajudou a impulsionar uma instalação posterior no mobile) | Não há uma visão específica para "instalações assistidas pela web". |
As jornadas web-to-app são mensuradas com Smart Script e Smart Banner. O relatório cross-platform apresenta uma análise semelhante, embora não seja idêntica. Assim como a PBA, é baseado no CUID:
|
| Recálculo retroativo | Não há recálculos retroativos além do atraso padrão de 30 minutos. | A atribuição é finalizada após um atraso de 30 minutos, permitindo que os usuários se identifiquem dentro desse período. |
| Campos de dados brutos | Campos relacionados ao mobile foram descontinuados para a web. Alguns campos foram renomeados e alguns valores mudaram. | Veja o apêndice sobre o mapeamento dos campos de dados brutos abaixo. |
| Eventos S2S tardios | Os eventos que chegam com um atraso de 30 minutos após o fim do dia (UTC) em que ocorreram ainda são aceitos, mas o horário é substituído pelo horário em que a AppsFlyer os recebeu. | Envie os eventos em tempo real, de preferência até 30 minutos após ocorrerem, em vez de agrupá-los em um único upload diário. A atribuição precisa depende do recebimento dos eventos próximo ao horário em que ocorrem. Isso alinha a web com o comportamento mobile existente. |
Apêndice: mudanças nos relatórios de dados brutos
Mapeamento único de todos os campos no relatório de dados brutos da PBA (visitas e eventos do site) ao novo relatório de eventos de usuários finais. Fonte: Relatórios de dados brutos da PBA.
Escopo: o relatório de visitas e eventos do site da PBA.
- Inalterado: mesmo nome do campo e mesmos dados.
- Renomeado: mesmos dados, mas novo nome da coluna.
- Redefinido: nome igual ou semelhante e mudanças nos dados ou no formato dos valores (analise antes de reutilizá-lo).
- Descontinuado: não há equivalente no novo relatório.
| Nome do campo | Descrição | Status | Novo campo/observação |
|---|---|---|---|
advertising_id |
Advertising ID (GAID) | Desativado | Mobile/Device Advertising ID, unificado entre plataformas na PBA. Relatórios de jornadas do usuário e de LTV cross-platform são baseados no CUID e não utilizam este campo. |
af_web_id |
Cookie ID enviado do Web SDK | Renomeado |
appsflyer_id_value, mesmo cookie da web. |
amazon_aid |
Advertising ID da Amazon Fire TV | Desativado | Mobile/Device ID; não relevante para a web. |
android_id |
Device ID do Android | Desativado | Mobile/Device ID; não relevante para a web. |
app_id |
ID do aplicativo instalado mais recentemente | Desativado | Campo mobile entre plataformas. O identificador do aplicativo web no novo relatório é unified_app_id ("site-{domínio}"): um conceito diferente. |
app_name |
Nome do aplicativo instalado mais recentemente | Inalterado |
app_name. |
app_version |
Versão do aplicativo instalado mais recentemente | Desativado | Campo mobile entre plataformas; não relevante para a web. |
appsflyer_id |
ID da AppsFlyer (instalação) | Desativado | ID da instalação mobile. O novo appsflyer_id_value é o cookie da web (af_web_id): um identificador distinto. |
attributed_touch_time |
Timestamp da visita à web (atribuída) | Renomeado |
event_time__attribution: o tempo atribuído ao toque (engajamento). |
attributed_touch_type |
Tipo de touchpoint; sempre "website visit" | Desativado | Valor constante na PBA. A distinção entre visita e evento agora é indicada em end_user_event_type (SESSION / IN_APP). |
bundle_id |
ID do bundle da PBA | Desativado | Substituído pelo agrupamento da linha de produtos (web + apps mobile) no relatório cross-platform. |
campaign |
Atribuído à visita ao site | Renomeado |
campaign_name. |
campaign_id |
Atribuído à visita ao site | Inalterado |
campaign_id. |
city |
Solucionado usando o endereço IP | Inalterado |
city. |
country_code |
Solucionado usando o endereço IP | Inalterado |
country_code. |
customer_user_id |
Customer User Identifier (CUID) | Inalterado |
customer_user_id. |
device_type |
Tipo de dispositivo | Renomeado |
device_category; os valores diferem ("DESKTOP" vs. "MOBILE_PHONE" / "TV"). |
dma |
Solucionado usando o endereço IP | Inalterado |
dma. |
event_name |
Visitas: sempre "website visit". Eventos: nome do evento enviado. | Redefinido | Vazio nas visitas (a PBA registrou "website visit"). Inalterado para eventos. |
event_revenue |
Valor da receita do evento na moeda de compra | Renomeado |
revenue_value_original. |
event_revenue_currency |
Código de moeda de 3 dígitos de event_revenue
|
Renomeado |
revenue_currency_original. |
event_revenue_usd |
event_revenue convertido em USD |
Renomeado |
revenue_usd. |
event_source |
Web SDK ou S2S | Inalterado |
event_source. |
event_time |
Visitas: horário da visita. Eventos: horário do evento. | Inalterado |
event_time. |
event_type |
Evento padrão/evento de conversão/visita ao site | Redefinido | A marcação de eventos de conversão não existe mais. A distinção entre visita e evento é indicada em end_user_event_type (SESSION / IN_APP). |
event_url |
URL da página da web onde ocorreu o evento (equivale ao URL original em visitas ao site) | Inalterado |
event_url agora também é o principal lugar para consultar os parâmetros de query do URL (UTMs, etc.). |
event_value |
Eventos: detalhes do evento como JSON. Visitas: null. | Inalterado |
event_value. |
idfa |
Identificador do anúncio | Desativado | Mobile/Device ID; não relevante para a web. |
idfv |
Identificador do anúncio | Desativado | Mobile/Device ID; não relevante para a web. |
imei |
Identificador de dispositivo | Desativado | Mobile/Device ID; não relevante para a web. |
install_time |
Horário da instalação mais recente do aplicativo | Desativado | Campo mobile entre plataformas. O horário da aquisição do usuário na web (conversão) é event_time__conversion: um conceito separado. |
ip |
Endereço IP do visitante | Renomeado |
ip_address_value; mesmo valor, e ip_address_type para o método de hash. |
language |
Informada pelo agente do usuário (por exemplo, "English") | Redefinido |
language; formato do valor alterado para ISO 639-1 + código de país. |
media_channel |
Atribuído à visita ao site ("Ad") | Desativado | Pode ser abordado pelo sub_param_1-5 se necessário. |
media_source |
Atribuído à visita ao site | Inalterado |
media_source. |
media_type |
Atribuído à visita ao site ("Paid") | Desativado | Vazio para web. A distinção entre orgânico e pago agora é determinada pelo is_organic booleano. |
oaid |
Identificador do anúncio | Desativado | Mobile/Device ID; não relevante para a web. |
original_url |
URL que redirecionava o usuário para a visita atribuída | Renomeado | Consolidado em event_url (nas visitas, event_url equivale ao URL original). A coluna independente foi removida. |
platform |
Plataforma ("macOS" e "Windows") | Redefinido | A plataforma é sempre "WEBSITE". O sistema operacional foi transferido para os_version / user_agent. |
postal_code |
Solucionado usando o endereço IP | Inalterado |
postal_code. |
query_params |
Parâmetros de consulta no URL de redirecionamento, como JSON | Desativado | A query string bruta é indicada em event_url; a coluna que continha o JSON foi removida. |
referrer |
HTTP referrer da visita atribuída ao site | Renomeado |
http_referrer. |
region |
Solucionado usando o endereço IP ("NA") | Renomeado |
continent; código do continente, por exemplo, aparece como "NA". |
state |
Solucionado usando o endereço IP | Inalterado |
state. |
Prompt predefinido com um agente de programação para a migração do ETL do Data Locker
Para facilitar essa transição, preparamos um prompt predefinido para seu agente de programação com IA (Claude Code, Cursor ou outro similar). Permita que o agente acesse seu código ETL existente e cole o prompt abaixo; ele contém o mapeamento completo do campo e todas as diferenças de comportamento. Isso orientará o agente a conhecer seu pipeline atual e recriá-lo no novo relatório.
You are migrating an ETL pipeline from AppsFlyer's legacy PBA Data Locker raw-data reports (Website visits + Website events) to AppsFlyer's Web Performance Measurement Data Locker report (End User Events). Everything you need is in this prompt: the structural changes, the complete field-by-field mapping, and the validation steps. Do not guess anything beyond what is written here; if something is ambiguous, ask me.
## Step 1: Learn the current pipeline
Before writing any code, explore the existing codebase and produce an inventory:
1. Which PBA reports we consume (Website visits, Website events, or both) and where they are ingested.
2. Every PBA field we read, and where each is used downstream (transforms, joins, dashboards, alerts, exports).
3. The load cadence and scheduling assumptions (PBA delivered daily).
4. Any logic that separates or joins visit rows and event rows.
5. Any filters, groupings, or hardcoded values keyed on PBA field values (e.g. media_source names, channel labels, event_type, platform values).
Present this inventory to me and wait for my confirmation before proceeding.
## Step 2: Ask me these questions
1. Is the new End User Events report already enabled in Data Locker (enabled from the AppsFlyer UI)? If not, I need to enable it first.
2. What is our new web app's Unified App ID? Format: "website-{domain}" (e.g. website-www.example.com). If I don't know it, I'll get it from the AppsFlyer dashboard before we continue.
3. Do we want a parallel-run period (old and new pipelines side by side, comparing outputs) or a direct cutover? Recommend parallel-run.
4. Should the new pipeline also consume the Conversions report (unique attribution instances, no duplicate rows) or the cross-platform End User Events report (CUID-deduplicated, web+mobile user-level)? Default scope is the platform-level End User Events report, the direct successor of the PBA reports.
## Structural changes to design around
1. **One report instead of two.** PBA split visits and events into two reports. The new End User Events report holds both: visits are rows with end_user_event_type = 'SESSION', events are rows with end_user_event_type = 'IN_APP'. Only IN_APP rows carry a valid event_name; on SESSION rows event_name is empty (PBA wrote "website visit" there).
2. **One schema across platforms.** The report is shared by website, mobile, CTV, and PC. Always filter platform = 'WEBSITE' to isolate web data.
3. **Dual attribution credit, deduplicate.** The same action can appear twice: a primary-credit row and a secondary-credit row (original UA source, for UA-view LTV). Filter is_primary_attribution = true by default to avoid double-counting. Drop the filter only for dedicated UA-view vs retargeting-view analysis using conversion_type.
4. **Hourly instead of daily.** The new report delivers hourly with approximately 2 hours of data freshness. Redesign incremental loads around hourly batches instead of a daily drop.
5. **Organic vs paid.** PBA's media_type ("Paid") is gone. Use the is_organic boolean; never infer organic/paid from media_source.
6. **User counting.** Count and join users with COALESCE(customer_user_id, appsflyer_id_value), the stable customer user ID first, the cookie-based ID as fallback.
7. **Empty values.** STRING fields are '', NUMERIC fields are NULL.
## Field-by-field mapping (PBA → End User Events)
Status legend: Unchanged = same name and data. Renamed = same data, new column. Redefined = data or value format changes (handle with care). Deprecated = no equivalent.
| PBA field | Status | New field / handling |
|---|---|---|
| advertising_id | Deprecated | Mobile/device ID. Cross-platform reports are CUID-based; drop it. |
| af_web_id | Renamed | appsflyer_id_value, same web cookie. |
| amazon_aid | Deprecated | Mobile/device ID, not relevant for web. |
| android_id | Deprecated | Mobile/device ID, not relevant for web. |
| app_id | Deprecated | Mobile cross-platform field. The new web app identifier is unified_app_id ("website-{domain}"), a different concept, not a rename. |
| app_name | Unchanged | app_name. |
| app_version | Deprecated | Mobile cross-platform field. |
| appsflyer_id | Deprecated | Mobile install ID. Note: the new appsflyer_id_value is the web cookie (PBA's af_web_id), NOT this field. |
| attributed_touch_time | Renamed | event_time__attribution. |
| attributed_touch_type | Deprecated | Was a constant. Visit-vs-event now lives in end_user_event_type. |
| bundle_id | Deprecated | Replaced by the Product Line grouping in the cross-platform report. |
| campaign | Renamed | campaign_name. |
| campaign_id | Unchanged | campaign_id. |
| city | Unchanged | city. |
| country_code | Unchanged | country_code. |
| customer_user_id | Unchanged | customer_user_id. |
| device_type | Renamed + values change | device_category, values differ (e.g. "Desktop" → "MOBILE_PHONE" / "TV" style values). Update any value-keyed logic. |
| dma | Unchanged | dma. |
| event_name | Redefined | Events: unchanged. Visits: now empty (PBA wrote "website visit"). Use end_user_event_type = 'SESSION' to identify visits. |
| event_revenue | Renamed | revenue_value_original. |
| event_revenue_currency | Renamed | revenue_currency_original. |
| event_revenue_usd | Renamed | revenue_usd. |
| event_source | Unchanged | event_source. |
| event_time | Unchanged | event_time. |
| event_type | Redefined | "Conversion event" marking no longer exists. Visit-vs-event lives in end_user_event_type (SESSION / IN_APP). |
| event_url | Unchanged | event_url, now also the primary place URL query params (UTMs, etc.) are read from. |
| event_value | Unchanged | event_value. |
| idfa / idfv / imei / oaid | Deprecated | Mobile/device IDs, not relevant for web. |
| install_time | Deprecated | Mobile field. The web user-acquisition time is event_time__conversion, a separate concept. |
| ip | Renamed | ip_address_value (plus ip_address_type for the hashing method). |
| language | Redefined | language, format changes to ISO 639-1 + country code (was e.g. "English"). |
| media_channel | Deprecated | Can be covered by sub_param_1-5 if needed. |
| media_source | Unchanged | media_source (but see value renames below). |
| media_type | Deprecated | Use the is_organic boolean instead. |
| original_url | Renamed | Consolidated into event_url (on visits, event_url equals the original URL). Standalone column gone. |
| platform | Redefined | platform is always "WEBSITE". The OS moved to os_version / user_agent. |
| postal_code | Unchanged | postal_code. |
| query_params | Deprecated | The raw query string lives in event_url; the parsed-JSON column is gone. Re-implement parsing from event_url if needed. |
| referrer | Renamed | http_referrer. |
| region | Renamed | continent, continent code (e.g. "NA"). |
| state | Unchanged | state. |
## Attributed-value changes (update filters and groupings)
The attribution engine resolves traffic sources differently, so some VALUES change even where field names don't:
- media_source renames: doubleclick_int → dv360_int (Google display/video via UTM); "X Ads" → "Twitter" (Twitter via UTM). More PID values now remap to display names (e.g. iossearchads_int → Apple Search Ads, metweb_int → Facebook Ads, tiktokweb_int → tiktokglobal_int, snapweb_int → snapchat_int).
- Channel labels change format: Direct / Organic search / Social media / Email / Ad / Referral / Other become DIRECT / ORGANIC_SEARCH / SOCIAL_MEDIA / EMAIL / AD / REFERRAL / OTHER.
- Click-ID coverage expanded (more networks resolve from click IDs); dclid alone no longer resolves; fbclid is not used.
Scan the codebase for any filter, CASE, join key, or dashboard grouping keyed on these old values and update them.
## Step 3: Build
1. Propose the new ETL design (ingestion, schema, incremental hourly logic, the mandatory filters from "Structural changes").
2. After my approval, implement it, reusing our existing conventions and infrastructure.
3. For every Deprecated field the inventory found in downstream use, list the consumer and propose a resolution (drop, replace with the suggested alternative, or flag to the business owner).
## Step 4: Validate
1. Run both pipelines on the same day range and compare: visits (SESSION rows) vs PBA website visits, events (IN_APP rows) vs PBA website events, and revenue totals.
2. Expect differences, not equality: the new attribution logic is more advanced, so attributed dimensions (media_source, campaign) will not match PBA exactly. Session counting logic is unchanged (30 minutes of activity), so visit volumes should be in the same ballpark.
3. Verify the is_primary_attribution filter is applied everywhere; its absence shows up as inflated event counts.
4. Produce a short migration report: what was mapped, what was dropped, what changed in values, and any open items for the business owner.
Apêndice: migração da API Web-S2S para a nova API S2S
Se você reporta eventos à PBA através da API de eventos de servidor para servidor (Web-S2S), mude para a nova API S2S. A nova API processa tanto visitas quanto eventos, permitindo que seu site opere totalmente do lado do servidor; a S2S da PBA aceita apenas eventos.
Endpoints e autenticação
| Web-S2S da PBA | Nova API S2S | |
|---|---|---|
| URL base | https://webs2s.appsflyer.com |
https://events.appsflyer.com |
| Chamada de evento | POST /v1/{bundleId}/event |
POST /v2.0/s2s/inapps/app/web/{appId} |
| Chamada de visita | Indisponível (as visitas eram provenientes apenas do Web SDK) | POST /v2.0/s2s/visits/app/web/{appId} |
| Chamada de identidade |
POST /v1/{bundleId}/setcuid (chamada separada para associar um CUID a um usuário da web) |
Nenhuma, a identidade é enviada diretamente no objeto user_id em cada evento/visita |
| Identificador do aplicativo |
bundleId (ID do Brand Bundle) no caminho |
appId = a web; unified_app_id ("site-{domínio}") no caminho |
| Autenticação |
webDevKey dentro do corpo JSON em todas as chamadas |
Cabeçalho de autorização que contém a S2S API key |
| Content type | application/json |
application/json |
| Resposta de sucesso | 200 OK | 202 Accepted |
Mapeamento do campo de payload
| Campo Web-S2S da PBA | Novo campo da S2S | Atenção |
|---|---|---|
customerUserId |
user_id.customer_user_id |
Agora dentro do objeto user_id. |
afUserId |
user_id.appsflyer_id |
Agora dentro do objeto user_id. Envie pelo menos um: customer_user_id ou appsflyer_id; envie ambos sempre que o usuário for identificado. |
webDevKey |
— | Removida do corpo. A autenticação foi transferida para o cabeçalho de autorização; o aplicativo é identificado por appId no caminho. |
eventType (sempre EVENT) |
— | Removido. O endpoint (/inapps vs. /visits) determina o tipo. |
eventName |
event_name |
1-64 caracteres; não pode conter @ = + -. |
timestamp (Unix em milissegundos, 13 dígitos) |
timestamp (Unix em milissegundos) |
Mesmo formato. Recomendado em todas as chamadas; se for omitido, a AppsFlyer usa o horário do recebimento. |
eventValue |
event_value |
Formato livre; aceita um sub-objeto custom_parameters. |
eventRevenue |
event_revenue |
Agora é opcional (a PBA exigia isso para dashboards). |
eventRevenueCurrency |
event_revenue_currency |
Agora é opcional (a PBA exigia isso para dashboards). |
referrer |
http_referrer |
Renomeado. |
userAgent |
user_agent |
Renomeado. |
ip |
ip |
Inalterado. |
| — | event_url |
Novo. Obrigatório nas visitas; opcional nos eventos. |
| — | customer_dedup_id |
Novo. Desduplica o mesmo evento quando ele é recebido de outra fonte (por exemplo, o Web SDK). |
| — |
email_hashed, phone_number_hashed, phone_number_e164_hashed, first_name_hashed, last_name_hashed
|
Novo. SHA256 (hexadecimal de 64 caracteres em letras minúsculas) de valores normalizados, para enriquecimento de identidade. |
Observações sobre a API S2S
- Agora as visitas ocorrem do lado do servidor. A S2S da PBA só aceitava eventos; as visitas eram provenientes do Web SDK. O novo endpoint /visits permite que o site funcione totalmente do lado do servidor.
- A chamada setcuid não é mais necessária. A PBA exigia o Web SDK, então era necessária uma chamada separada para associar um CUID a um usuário da web. A nova API mantém a identidade diretamente em cada solicitação, então esta etapa deixa de ser necessária.
- Janela para dados atrasados. Envie os eventos e visitas em tempo real, de preferência até 30 minutos após ocorrerem, em vez de agrupá-los em um único upload diário. Os dados que chegam com um atraso de 30 minutos após o fim do dia (UTC) em que o evento ocorreu ainda são aceitos, mas o horário é substituído pelo horário em que a AppsFlyer os recebeu.
Migração do serviço S2S: prompt predefinido com agente de programação
Se você reporta eventos através da API S2S da PBA, preparamos um prompt predefinido para seu agente de programação com IA (Claude Code, Cursor ou outro similar). Permita que o agente acesse o serviço que envia os eventos para a AppsFlyer e cole o prompt abaixo; ele contém o mapeamento completo do endpoint e do payload. Isso orientará o agente a conhecer sua implementação atual e criar uma versão atualizada.
You are migrating a server-side integration from AppsFlyer's legacy PBA Web S2S events API (webs2s.appsflyer.com) to AppsFlyer's new S2S API for Web Performance Measurement (events.appsflyer.com). Everything you need is in this prompt: endpoints, authentication, the complete payload mapping, and the behavior changes. Do not guess anything beyond what is written here; if something is ambiguous, ask me.
## Step 0: Ask me these questions before touching code
1. Should we modify the existing service in place, or create a new service/module alongside it (allowing a parallel-run and clean cutover)? Recommend a new module alongside.
2. Do I know our new web app's Unified App ID? Format: "website-{domain}" (e.g. website-www.example.com). It replaces PBA's bundle ID in the URL path. If I don't know it, I'll get it from the AppsFlyer dashboard before we continue.
3. Do I have the new S2S API key? Authentication moved from the webDevKey in the request body to an Authorization header carrying this key. If I don't have it, I'll retrieve it from the AppsFlyer dashboard.
4. Do we want to send events only (like PBA), or adopt the new visits endpoint too? The new API supports server-side visits, so the website can run fully server-side; PBA accepted events only.
5. Is the AppsFlyer Web SDK still running on our site? (Determines whether visits come from the SDK and whether we need event deduplication between SDK and S2S.)
## Step 1: Learn the current service
Explore the codebase and produce an inventory:
1. Every call site to webs2s.appsflyer.com, the /event calls and any /setcuid calls.
2. The fields populated on each call (customerUserId, afUserId, eventName, eventValue, eventRevenue, timestamp, referrer, userAgent, ip, etc.) and where their values come from.
3. Error handling and monitoring keyed on the 200 OK response.
4. Retry, batching, and queueing behavior.
Present this inventory to me and wait for my confirmation before proceeding.
## What changed: endpoints and authentication
| | PBA Web-S2S (old) | New S2S API |
|---|---|---|
| Base URL | https://webs2s.appsflyer.com | https://events.appsflyer.com |
| Event call | POST /v1/{bundleId}/event | POST /v2.0/s2s/inapps/app/web/{appId} |
| Visit call | Not available | POST /v2.0/s2s/visits/app/web/{appId} |
| Identity call | POST /v1/{bundleId}/setcuid | None, identity is inline in the user_id object on every call |
| App identifier in path | bundleId (brand bundle ID) | appId = the web Unified App ID ("website-{domain}") |
| Authentication | webDevKey in the JSON body | Authorization header with the S2S API key |
| Content type | application/json | application/json (415 if missing) |
| Success response | 200 OK | 202 Accepted |
## Payload field mapping
| PBA field | New field | Note |
|---|---|---|
| customerUserId | user_id.customer_user_id | Nested in the user_id object. |
| afUserId | user_id.appsflyer_id | Nested in the user_id object. Send at least one of customer_user_id or appsflyer_id; send both whenever the user is identified. |
| webDevKey | (removed) | Auth moved to the Authorization header; the app is identified by appId in the path. |
| eventType (always "EVENT") | (removed) | The endpoint (/inapps vs /visits) determines the type. |
| eventName | event_name | 1-64 chars; cannot contain @ = + - characters. |
| timestamp (Unix ms) | timestamp (Unix ms) | Same format. Recommended on every call; if omitted, AppsFlyer uses receive time. |
| eventValue | event_value | Free-form; supports a custom_parameters sub-object. |
| eventRevenue | event_revenue | Now optional (PBA required it for dashboards). |
| eventRevenueCurrency | event_revenue_currency | Now optional (PBA required it for dashboards). |
| referrer | http_referrer | Renamed. |
| userAgent | user_agent | Renamed. |
| ip | ip | Unchanged. |
| (new) | event_url | Required on visits; optional on events. |
| (new) | customer_dedup_id | Deduplicates against the same event arriving from another source (e.g. the Web SDK). |
| (new) | email_hashed, phone_number_hashed, phone_number_e164_hashed, first_name_hashed, last_name_hashed | SHA256 (64-char lowercase hex) of normalized values, for identity enrichment. |
## Behavior changes and gotchas
1. **appId is the Unified App ID, not the "Web SDK ID".** The AppsFlyer app settings page shows a "Web SDK ID" UUID (the former Web Dev Key, kept for SDK continuity). The S2S path must carry the Unified App ID ("website-{domain}"), never that UUID. A 401 "app not found" usually means a wrong appId in the path or a missing/invalid Authorization header.
2. **Success is 202, not 200.** Update health checks, retries, and alerting accordingly.
3. **Don't validate against the old endpoint.** The legacy endpoint may still return 200, but that response is not proof that data reached Web Performance Measurement. Always verify events actually appear in the new web app's data.
4. **Drop the setcuid flow entirely.** Identity travels inline in the user_id object on every event and visit.
5. **Visits before events.** The system expects a visit before any event from a user; events for a user with no prior visit are classified as organic. Visits come from the Web SDK, or, new, from the /visits endpoint.
6. **Send in real time; don't batch.** Send every event and visit as it happens, preferably within 30 minutes of the event occurring. Data that arrives later than 30 minutes after the end of the UTC day in which the event occurred is still accepted, but its event time is replaced with the receive time, which distorts attribution. Always send a timestamp. Note that the approximately 30-minute attribution delay you may read about is a separate, server-side hold on AppsFlyer's side, nothing for us to implement.
7. **Full server-side option (if we adopt visits).** With no Web SDK, our server owns the web user identifier: generate a stable ID for first-time visitors, persist it as a server-set first-party HTTP cookie (Set-Cookie header, not JavaScript; JS cookies are capped at approximately 7 days on Safari), reuse it on every request, and send it as user_id.appsflyer_id. The visit payload must include event_url (and should include ip, user_agent, http_referrer for attribution quality).
8. **SDK + S2S together.** If both send the same event, populate customer_dedup_id so AppsFlyer keeps one copy.
## Step 2: Build
1. Propose the design: new client/module, config (base URL, appId, API key storage in our secrets manager, never hardcoded), payload builders for events (and visits, if in scope), and the response/retry handling for 202.
2. After my approval, implement it following our existing conventions.
3. Map every field from the inventory through the payload mapping above; flag any field we currently send that has no new equivalent.
## Step 3: Validate
1. Send a test event (and visit, if in scope) and confirm a 202 response.
2. Verify the test data appears in the new web app in AppsFlyer (dashboard or Data Locker); this is the real success signal, not the HTTP response.
3. Confirm event names comply with the new constraints (1-64 chars, no @ = + -).
4. If running in parallel with the old service, compare event volumes between old and new for a few days before cutover, then decommission the old calls including setcuid.
Apêndice: identificação de fontes de tráfego - PBA vs. mensuração da performance web
Se os valores da fonte de mídia, do canal ou da campanha são diferentes do que a PBA reportou, vamos explicar o motivo. Ambas as soluções usam a mesma abordagem implícita: identificam a fonte de mídia a partir dos parâmetros de URL e do referrer na visita à web, vasculhando uma lista de prioridades e usando a primeira correspondência. Mas várias regras mudaram, e essas alterações também mudaram os valores exibidos nos relatórios. Fontes: Regras de atribuição de fontes de mídia na PBA e Identificação de fontes de tráfego.
Valores de fontes de mídia renomeados
| Trigger | Valor na PBA | Novo valor |
|---|---|---|
| Google display/video (utm_source=Google + utm_medium= cpm / display/banner/video/listing) | doubleclick_int |
dv360_int |
| Twitter via UTM (twitter + cpc) | X Ads |
A mensuração da performance web também mapeia novamente outros valores de PID para nomes de display. Por exemplo, iossearchads_int para Apple Search Ads, metweb_int para Facebook Ads, twitterweb_int para Twitter, tiktokweb_int para tiktokglobal_int, snapweb_int para snapchat_int. Os relatórios baseados nos valores brutos antigos precisam ser ajustados para os novos valores.
A cobertura do Click ID foi ampliada e o dclid deixou de ser necessário
A PBA identificou os IDs de três cliques: gclid para googleadwords_int, dclid para doubleclick_int, e msclkid para bingsearch_int.
A mensuração da performance web identifica muitos outros, gclid/wbraid/gbraid para googleadwords_int, msclkid para bingsearch_int, twclid para Twitter, vmcid para yahoogemini_int, sccid para snapchat_int, li_fat_id para linkedin_int, ttclid para tiktokglobal_int, tbclid para taboola_int, ob_click_id/dicbo para outbrain_int, yclid para yandex_int, rdt_cid para reddit_int.
Aviso
Duas mudanças de comportamento: dclid já não é utilizado isoladamente para a identificação (a PBA classificou-o como doubleclick_int), e fbclid é explicitamente não utilizado.
Regras personalizadas de UTM ampliadas
O mapeamento utm_source + utm_medium personalizado é mais abrangente na mensuração da performance web. Ele inclui mais variações de medium (paid, paid_search, paid-search, etc.) e cobertura ampliada para TikTok, Snapchat e Pinterest além das regras restritas da PBA a um único medium. Onde não há correspondências de regras personalizadas, ambos recorrem ao valor utm_source bruto.
Canal
O formato dos valores de canal também mudou: Direct / Organic search / Social media / Email / Advertising / Referral / Other para DIRECT / ORGANIC_SEARCH / SOCIAL_MEDIA / EMAIL / AD / REFERRAL / OTHER. Os filtros e agrupamentos que usavam as etiquetas antigas precisam de ser atualizados.