How can we help?

[BETA]从PBA迁移至网页端效果衡量

  • 更新

概览:AppsFlyer已对网页端衡量解决方案进行升级,旧版基于用户的归因(PBA)将更新为「网页端效果衡量」。本文将为您介绍本次升级的具体变化和迁移步骤,以及报告和服务器端对接所涉及的完整字段映射。

为何升级网页端衡量方案

网页端衡量的重要性日益凸显。许多用户会在网站上完成转化,网站也是用户进入移动应用的重要起点。网站不只是落地页,还承载着问答流程、个性化新客引导、付费墙和网页端商店等完整获客流程,往往能够以更低的获客成本吸引意向更高的用户。

AppsFlyer正在以移动端的衡量标准对网页端衡量能力进行更新。旧版网页端产品基于用户的归因(PBA)将更新为「网页端效果衡量」。新版解决方案基于AppsFlyer核心归因引擎和统一数据模型构建。除PBA现有的全部功能外,网页端效果衡量还新增了以下功能:

  • 单一可信数据源:在同一平台衡量网页端和移动应用,并统一进行成本汇总、优化回传和素材优化。
  • 优化预算决策:根据业务逻辑灵活配置归因,将更多预算投入真正有效的广告系列。
  • 提升ROAS:向广告平台发送更丰富的优化回传,并通过完整的服务器端配置衡量更多转化,让广告平台利用更优质的信号进行优化。
  • 衡量真正的跨平台ROI:衡量用户在网页端、应用及其他平台上的完整旅程。
  • 简洁统一的报表:通过统一的数据面板和报表查看所有平台的数据,每小时更新一次。

注意

仅需简单操作即可完成迁移。您可以继续使用原有的Web SDK,无需修改网站代码。

重要提示!

网页端效果衡量采用更先进、更灵活的归因逻辑,因此迁移后数据结果可能会与PBA存在差异。

网页端效果衡量与PBA对比

功能 说明 与PBA对比 网页端效果衡量
数据
数据时效性 数据多久后可在报表中显示 ✗每天更新 ✓报表每小时更新,数据延迟约2小时
数据模型 数据结构与移动端归因数据是否一致 ✗与移动端不同 ✓与移动端一致,便于合并和分析
报表
活跃数据面板 按事件发生时间统计的数据面板 ✓支持 ✓支持
群组面板 获客群组表现随时间变化 ✗不支持 ✓支持
Data Locker原始数据 通过Data Locker导出并访问原始归因数据 ✓支持 ✓支持
广告层级数据颗粒度 报告可从广告系列逐层细分至广告层级 ✗仅面板支持,原始数据不支持 ✓面板和原始数据均支持完整的广告系列层级粒度
跨平台LTV 打通网页端和移动端用户旅程,呈现统一的LTV ✗不支持 ✓支持
成本与信号
网页端成本 接收并报告网页端广告的支出数据 ✗不支持 ✓支持
优化回传 向广告平台回传优化信号 ✗不支持 ✓支持
实施
网页端SDK(Pixel) 用于衡量网页访问和事件的客户端SDK ✓支持 ✓沿用原有SDK,无需对代码进行调整
S2S支持 对访问和事件进行完整的服务器端衡量 ✗仅支持事件 ✓支持事件和访问可通过服务器端实施。
归因引擎
归因窗口 可配置回溯窗口、归因窗口、再互动窗口和非活跃窗口 ✗仅支持点击回溯窗口 ✓支持5个可配置窗口(完全掌控)
自定义UA事件 定义除首次访问外还定义哪些事件属于获客事件 ✗仅限首次访问 ✓支持任意自定义事件,例如注册、购买
UA与再营销 分别对新用户获取和再互动进行归因 ✗无UA概念,事件归因至最终触点 ✓提供独立的UA和再营销视图,并支持双重归因

迁移步骤

下表将迁移流程分为5个步骤。所有客户均需创建网页端应用;其余步骤取决于当前使用的PBA功能,例如S2S API或Data Locker报告。

操作 适用对象 操作说明
创建网页端应用 所有客户 创建新的网页端应用时,在网页端SDK ID字段中选择现有的网页端Dev Key。该操作可为您省去更改代码的步骤,网站上的现有代码即可继续正常运行。随后,在新应用中配置归因设置。
迁移至新版S2S API 使用PBA S2S API的客户 设置新版S2S API。新版API同时支持访问和事件,可完全通过服务器端实施。请参阅下方的S2S API迁移附录
迁移至新版Data Locker报告 使用PBA Data Locker报告的客户 在UI中启用新版网页端报告,并将BI/ETL切换至更新后的数据。新版报告采用跨平台统一的数据结构,涵盖移动应用、网站、CTV和PC。请参阅下方的原始数据字段映射附录
检查归因参数 建议执行 PBA采用独立的媒体渠道归因规则。网页端效果衡量采用优化后的流量来源识别方式,提高分析准确性。请查看两者之间的差异,了解报告中可能出现的数据结果。请参阅下方的流量来源识别对比附录
将应用归入同一产品线 可选 创建产品线,并将网页端应用与移动应用归入同一产品线,即可使用跨平台用户LTV报告,功能类似于PBA Brand Bundle。

注意

网页端效果衡量采用更先进、更灵活的归因逻辑,因此迁移后数据结果可能会与PBA存在差异。应用打开记录逻辑保持不变。按照行业标准,系统仍会将30分钟内的用户活动记录为同一次应用打开。

停用的功能

功能 具体变化 详细说明
转化路径 已弃用。
网页端助攻激活(网页端广告系列推动用户后续完成移动端激活,并获得助攻归因) 不再提供专门的「网页端助攻激活」视图。

Web-to-app用户旅程可通过智能脚本和智能横幅进行衡量。跨平台报告可提供类似的分析,但结果并不完全相同。与PBA一样,该报告基于CUID:

  • 此前由网页端广告系列助攻、并报告为自然激活的用户,现在将显示为非自然用户获取。此类情况不再视为助攻,网页端广告系列将作为获客来源。
  • 如果网页端广告系列助攻了一次非自然激活,网页端广告系列将被视为用户获取来源,移动端激活则会被视为跨平台再营销。
回溯重算 除默认延迟的30分钟外,系统不会回溯重算此前数据。 系统将在延迟30分钟内确定归因结果,用户可在此期间完成身份识别。
原始数据字段 网页端不再支持移动端相关字段。部分字段将重命名,部分字段值也会发生变化。 请参阅下方的原始数据字段映射附录
延迟传入的S2S事件 如果事件在发生当日UTC时间结束30分钟后才传入,系统仍会接收该事件,但会将事件时间替换为AppsFlyer收到事件的时间。 请实时发送事件,最好在事件发生后30分钟内发送,不要将事件汇总后每日一次性上传。为确保归因准确,请尽量在事件发生后及时传入数据。这会使网页端与移动端现有的处理逻辑保持一致。

附录:原始数据报告变更

本附录逐一列出了PBA原始数据报告(网站访问和网站事件)中所有字段与新版终端用户事件报告字段之间的映射关系。来源:PBA原始数据报告

范围:PBA网站访问和网站事件报告。

  • 未变更:字段名称和数据均保持不变。
  • 已更名:数据保持不变,但列名称已更新。
  • 已重新定义:字段名称相同或相近,但数据或值格式发生变化,继续使用前需仔细检查。
  • 已弃用:新版报告中没有对应字段。
字段名称 说明 状态 新字段/备注
advertising_id 广告ID(GAID) 已弃用 移动端/设备广告ID,PBA使用该字段关联跨平台数据。跨平台LTV和用户旅程报告基于CUID,不使用此字段。
af_web_id 由网页端SDK发送的Cookie ID 已更名 appsflyer_id_value,对应同一个网页端Cookie。
amazon_aid Amazon Fire TV广告ID 已弃用 移动端/设备ID,不适用于网页端。
android_id Android设备ID 已弃用 移动端/设备ID,不适用于网页端。
app_id 最近安装的应用ID 已弃用 移动端跨平台字段。新版报告中的网页端应用标识符为unified_app_id(「website-{domain}」),两者含义不同。
app_name 最近安装的应用名称 未变更 app_name
app_version 最近安装的应用版本 已弃用 移动端跨平台字段,不适用于网页端。
appsflyer_id AppsFlyer(激活)ID 已弃用 移动端激活ID。新版appsflyer_id_value为网页端Cookie(af_web_id),属于不同的标识符。
attributed_touch_time 归因网站访问的时间戳 已更名 event_time__attribution,即归因触点(广告交互)的发生时间。
attributed_touch_type 触点类型,固定为「web visit」 已弃用 该字段在PBA中为固定值。目前通过end_user_event_type(SESSION/IN_APP)区分访问和事件。
bundle_id PBA组合ID 已弃用 跨平台报告改用产品线分组,将网页端应用和移动应用归入同一产品线。
campaign 归因至网站访问 已更名 campaign_name
campaign_id 归因至网站访问 未变更 campaign_id
city 根据IP地址识别 未变更 city
country_code 根据IP地址识别 未变更 country_code
customer_user_id 客户用户ID(CUID) 未变更 customer_user_id
device_type 设备类型 已更名 device_category,字段值有所不同(「Desktop」与「MOBILE_PHONE」/「TV」)。
dma 根据IP地址识别 未变更 dma
event_name 访问:固定为「website visit」。事件:传入的事件名称 已重新定义 访问记录中为空(PBA中记录为“website visit”)。事件记录保持不变。
event_revenue 以购买币种计算的事件收入金额 已更名 revenue_value_original
event_revenue_currency event_revenue对应的3位币种代码 已更名 revenue_currency_original
event_revenue_usd event_revenue换算为美元后的金额 已更名 revenue_usd
event_source 网页端SDK或服务器到服务器(S2S) 未变更 event_source
event_time 访问:访问时间。事件:事件发生时间 未变更 event_time
event_type 标准事件/转化事件/网站访问 已重新定义 不再提供转化事件标记。目前通过end_user_event_type(SESSION/IN_APP)区分访问和事件。
event_url 事件发生页面的URL(对于访问记录,该值与原始URL相同) 未变更 event_url,目前也是获取URL查询参数(UTM等)的主要字段。
event_value 事件:JSON格式的事件详情。访问:空值 未变更 event_value
idfa 广告标识符 已弃用 移动端/设备ID,不适用于网页端。
idfv 广告标识符 已弃用 移动端/设备ID,不适用于网页端。
imei 设备标识符 已弃用 移动端/设备ID,不适用于网页端。
install_time 最近一次应用激活时间 已弃用 移动端跨平台字段。网页端用户获取(转化)时间由event_time__conversion记录,两者含义不同。
ip 访客IP地址 已更名 ip_address_value,字段值不变;另新增ip_address_type,用于记录哈希处理方式。
language User Agent报告的语言(例如「English」) 已重新定义 language,字段值格式改为ISO 639-1语言代码和国家/地区代码。
media_channel 归因至网站访问(「Ad」) 已弃用 如有需要,可通过sub_param_1-5获取相关信息。
media_source 归因至网站访问 未变更 media_source
media_type 归因至网站访问(「Paid」) 已弃用 网页端记录中为空。目前通过is_organic布尔值区分自然流量和付费流量。
oaid 广告标识符 已弃用 移动端/设备ID,不适用于网页端。
original_url 归因访问中的重定向URL 已更名 已合并至event_url(在访问记录中,event_url与原始URL相同)。不再提供单独的列。
platform 平台(「macOS」「Windows」) 已重新定义 平台固定为「WEBSITE」。操作系统信息已移至os_version/user_agent
postal_code 根据IP地址识别 未变更 postal_code
query_params 重定向URL中的查询参数,以JSON格式记录 已弃用 原始查询字符串保存在event_url中,不再提供解析后的JSON列。
referrer 归因网站访问的HTTP Referrer 已更名 http_referrer
region 根据IP地址识别(「NA」) 已更名 continent,即大洲代码,例如「NA」。
state 根据IP地址识别 未变更 state

用于Data Locker ETL迁移的AI编程智能体提示词

为了简化迁移流程,我们准备了一段可以直接用于AI编程智能体(Claude Code、Cursor或类似工具)的提示词。授予智能体访问现有ETL代码的权限,然后粘贴下方提示词。提示词中包含完整的字段映射和所有处理逻辑差异。智能体将分析当前的数据管道,并根据新版报告重新构建数据管道。

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.

附录:从PBA Web-S2S迁移至新版S2S API

如果目前通过网页端服务器到服务器事件API(Web-S2S)向PBA报告事件,请迁移至新版S2S API。新版API同时支持访问和事件,因此网站可以完全通过服务器端运行;PBA S2S仅支持接收事件。

端点与身份验证

PBA Web-S2S 新版S2S API
基础URL https://webs2s.appsflyer.com https://events.appsflyer.com
事件调用 POST /v1/{bundleId}/event POST /v2.0/s2s/inapps/app/web/{appId}
访问调用 不支持(访问数据只能通过网页端SDK传入) POST /v2.0/s2s/visits/app/web/{appId}
身份关联调用 POST /v1/{bundleId}/setcuid(用于将CUID与网页端用户关联的单独调用) 无需单独调用。身份信息通过每个事件或访问的user_id对象直接传入。
应用标识符 路径中的bundleId(品牌组合ID) 路径中的appId=网页端unified_app_id(「website-{domain}」)
身份验证 每次调用时,在JSON正文中传入webDevKey 在Authorization请求头中传入S2S API密钥
内容类型 application/json application/json
成功响应 200 OK 202 Accepted

请求数据字段映射

PBA Web-S2S字段 新版S2S字段 备注
customerUserId user_id.customer_user_id 现已嵌套在user_id对象中。
afUserId user_id.appsflyer_id 现已嵌套在user_id对象中。至少传入customer_user_idappsflyer_id中的一个;识别出用户身份后,请同时传入两个字段。
webDevKey 已从请求正文中移除。身份验证已移至Authorization请求头,应用则通过路径中的appId进行识别。
eventType(固定为EVENT) 已移除。类型由端点(/inapps或/visits)决定。
eventName event_name 长度为1-64个字符,不能包含@ = + -
timestamp(Unix毫秒时间戳,13位) timestamp(Unix毫秒时间戳) 格式不变。建议每次调用时均传入;如果未传入,AppsFlyer将使用收到数据的时间。
eventValue event_value 支持自由格式,并可包含custom_parameters子对象。
eventRevenue event_revenue 现为可选字段(PBA要求必须传入,才能在面板中显示)。
eventRevenueCurrency event_revenue_currency 现为可选字段(PBA要求必须传入,才能在面板中显示)。
referrer http_referrer 已重命名。
userAgent user_agent 已重命名。
ip ip 未更改。
event_url 新增字段。访问调用必填,事件调用可选。
customer_dedup_id 新增字段。用于对来自其他来源(例如网页端SDK)的同一事件进行去重。
email_hashedphone_number_hashedphone_number_e164_hashedfirst_name_hashedlast_name_hashed 新增字段。对标准化后的值进行SHA256哈希处理(64位小写十六进制),用于补充身份信息。

S2S API注意事项

  • 访问现已支持通过服务器端传入。PBA S2S仅支持接收事件,访问数据必须通过网页端SDK传入。新版/visits端点支持网站完全通过服务器端运行。
  • 不再需要调用setcuid。PBA需要使用网页端SDK,因此必须单独调用接口,将CUID与网页端用户关联。新版API会在每次请求中直接传入身份信息,因此不再需要执行此步骤。
  • 延迟数据接收窗口。请实时发送事件和访问数据,最好在事件发生后30分钟内发送,不要将数据汇总后每日一次性上传。如果数据在事件发生当日UTC时间结束30分钟后才传入,系统仍会接收数据,但会将事件时间替换为AppsFlyer收到数据的时间。

用于S2S服务迁移的AI编程智能体提示词

如果目前通过PBA服务器到服务器API报告事件,可以使用我们为AI编程智能体(Claude Code、Cursor或类似工具)准备的提示词。授予智能体访问AppsFlyer事件发送服务的权限,然后粘贴下方提示词。提示词中包含完整的端点和请求数据映射。智能体将分析当前的实施方式,并完成新版API的实施。

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.

附录:PBA vs 网页端效果衡量 - 流量来源识别对比

如果媒体渠道、渠道或广告系列值与PBA报告中的结果不同,您可以在本节了解具体原因。两种方案采用相同的基本逻辑:根据网站访问中的URL参数和HTTP Referrer识别媒体渠道,并按照优先级顺序逐项匹配,以首个匹配结果为准。但部分规则已发生变化,因此报告中显示的值也会有所不同。来源:PBA媒体渠道归因规则流量来源识别简介

媒体渠道值更名

触发条件 PBA值 新值
Google展示广告/视频广告(utm_source=Google+utm_medium=cpm/display/banner/video/listing) doubleclick_int dv360_int
通过UTM识别的Twitter(twitter+cpc) X Ads Twitter

网页端效果衡量还会将更多PID值映射为对应的显示名称,例如将iossearchads_int映射为Apple Search Ads、metweb_int映射为Facebook Ads、twitterweb_int映射为Twitter、tiktokweb_int映射为tiktokglobal_intsnapweb_int映射为snapchat_int。基于旧版原始值构建的报告需要调整,以适配新的字段值。

扩大Click ID覆盖范围,不再使用dclid

PBA可识别3种Click ID:将gclid识别为googleadwords_intdclid识别为doubleclick_intmsclkid识别为bingsearch_int

网页端效果衡量支持识别更多Click ID:将gclid/wbraid/gbraid识别为googleadwords_intmsclkid识别为bingsearch_inttwclid识别为Twitter、vmcid识别为yahoogemini_intsccid识别为snapchat_intli_fat_id识别为linkedin_intttclid识别为tiktokglobal_inttbclid识别为taboola_intob_click_id/dicbo识别为outbrain_intyclid识别为yandex_intrdt_cid识别为reddit_int

注意

处理逻辑有两项变化:系统不再单独使用dclid识别流量来源(PBA会将其识别为doubleclick_int),并且明确不使用fbclid

扩展UTM自定义规则

网页端效果衡量扩大了自定义utm_source+utm_medium映射的覆盖范围,支持更多medium同义值(paid、paid_search、paid-search等),并新增对TikTok、Snapchat和Pinterest的支持。相比之下,PBA仅支持少量单一medium规则。如果没有匹配的自定义规则,两种方案均会使用utm_source的原始值。

渠道

渠道的格式也会发生变化,由PBA中的Direct/Organic search/Social media/Email/Ad/Referral/Other改为DIRECT/ORGANIC_SEARCH/SOCIAL_MEDIA/EMAIL/AD/REFERRAL/OTHER。使用旧版标签设置的筛选条件和分组需要相应更新。

This article was translated automatically and may contain errors. The English version is the most accurate - use the language selector below to switch.

Share article: