服务器到服务器--集成指南
实施 Singular 的 REST API,进行完整的服务器端跟踪,作为 SDK 集成的替代方案,实现对数据收集、传输和归属工作流程的全面控制。
概述
服务器到服务器使用案例
服务器到服务器(S2S)集成提供 REST API 端点,用于构建完整的归因和分析解决方案,这些解决方案从您的后端基础设施运行,无需在客户端应用程序中嵌入 Singular SDK。
集成方法:
- 纯 S2S:100% 服务器端实施,同时处理会话和事件跟踪
- 混合:Singular SDK 管理会话,服务器端处理事件跟踪
混合集成模式
混合集成将用于会话管理的 Singular SDK 与用于后端事件跟踪的服务器端 EVENT API 相结合,在易于实施与服务器端灵活性之间取得平衡。
混合优势:
- SDK 自动处理复杂的会话逻辑、深度链接和设备数据收集
- 服务器为后台系统处理的事务发送事件
- 降低客户端实施的复杂性
- 不需要会话端点--SDK 管理会话生命周期
设备数据检索方法:
- 客户端管理流程:在客户端捕获所需的数据点,并通过内部应用程序接口转发到服务器,以便与 Singular EVENT 端点一起使用
- 内部 BI 回传:配置 Singular 内部 BI 回传,以便在安装、重新参与或事件后接收带有设备标识符的实时 JSON 有效载荷(设置指南
设备图维护:这两种方法都需要服务器端逻辑来维护设备图。当 SDK 检测到设备标识符发生变化时,相应更新服务器以确保准确跟踪。
实施资源:
- EVENT 端点所需参数
- 检索设备数据指南(iOS/Android 代码示例
主要集成原则
| 原则 | 说明 |
|---|---|
| 灵活性 | 完全控制数据收集和传输时间 |
| 功能奇偶性 | 在提供适当数据的情况下,支持所有 SDK 功能 |
| 集成路径 | 客户端 → 服务器 → Singular API |
| 实时处理 | 一次处理一个请求,不支持批量处理 |
| 顺序流程 | 事件必须按时间顺序处理 |
| 无重复数据删除 | Singular 不重复数据删除--执行服务器端重复数据删除 |
| 数据永久性 | 设备级数据在摄取后不能删除-在发送前进行验证 |
集成要求
纯 S2S 集成需要实施全面的数据管道,以进行会话和事件跟踪。
- 数据收集:从客户端应用程序收集所需的数据点
- 设备图:将数据转发到服务器并维护设备标识符存储
- 会话请求:通过SESSION API发送会话通知
- 响应处理:处理 Singular 响应并将其转发回客户端应用程序
- 事件请求:通过EVENT API转发事件
REST API 端点
Singular 提供了两个主要的 REST API 端点,用于服务器到服务器的会话和事件跟踪。
会话端点
会话跟踪 API
SESSION 端点通知 Singular 应用程序打开事件,以初始化用户会话,用于归因和保留跟踪。
GET https://s2s.singular.net/api/v1/launch
完整参考资料:会话端点 API 参考资料
事件端点
事件跟踪 API
EVENT 端点跟踪应用内事件和收入,用于归因分析和营销活动优化。
GET https://s2s.singular.net/api/v1/evt
完整参考资料:EVENT 端点 API 参考资料
实施阶段
成功的 S2S 整合需要依次执行四个关键实施阶段,以获得最佳的数据质量和归因准确性。
第 1 阶段:数据收集
所需数据点
建立健全的数据收集策略,捕捉 Singular 平台功能所需的所有参数。
所有必填参数都必须填写:没有参数是可选的。
异步函数处理:收集客户端数据用于服务器传输时,等待异步函数完成并处理边缘情况。导致数据缺失和部分归属的常见问题。
实施资源:
- 会话端点所需参数
- EVENT 端点所需参数
- 检索设备数据指南(iOS/Android 代码示例
- SKAdNetwork 4 实施指南(iOS 特定数据点
第 2 阶段:实时流
关键时间要求
实时数据流可保持归因的准确性,并启用 SKAdNetwork 转换值更新等时间敏感型功能。
归因影响:
- 会话延迟:严重影响归因准确性--系统需要精确的时间数据来关联营销活动
- SKAdNetwork 定时器:转换值在设备上的严格定时窗口使得实时流变得至关重要。延迟会导致错过转换值更新和活动数据不完整
最佳实践:
- 为应用程序会话启动实施服务器端事件监听器
- 立即转发包含所有所需参数的会话数据
- 为应用内事件实施服务器端事件监听器
- 立即转发包含所有所需参数的事件数据
- 使用 Webhook 架构实现可靠的数据传输
- 针对失败请求实施重试机制
- 监控数据流以保证质量
第 3 阶段:响应处理
双向通信
响应处理是服务器端 API 与客户端功能交互的桥梁,可实现延迟深度链接和转换值更新。
关键响应类型:
- 延迟深度链接:API 响应包含待处理的深度链接数据,需要立即转发给应用程序,以便进行用户路由和个性化设置
- 转换值:iOS SKAdNetwork 转换值必须及时转发给应用程序,以便准确测量营销活动
最佳实践:
- 在服务器基础设施上实施响应处理
- 解析和验证 Singular API 响应
- 将相关响应数据转发给客户端应用程序(对 iOS SKAdNetwork 至关重要)
- 实施客户端响应处理
- 使用适当的 HTTP 状态代码优雅地处理错误
- 为重试机制记录失败的响应
第 4 阶段:测试与验证
数据流验证
测试阶段在生产部署前验证完整的数据管道功能和归因准确性。
会话归因流程:
- 第一次会话(新安装):单点识别新安装并触发安装归因流程
- 再接触合格:Singular 触发再参与归因流程(再参与常见问题
- 标准会话:Singular 会记录会话,以获得用户活动和保留指标
关键时间要求:
- 活动前会话:单一会话必须在任何事件发生前收到。SDK 会在应用打开时触发会话,然后发送应用内事件。经过 1+ 分钟的后台时间后,会话超时。当应用程序返回前台时,会发送新会话。使用应用程序生命周期事件和计时器进行会话管理
- 实时事件:应用程序中发生的事件必须在各自会话结束后实时发送
验证清单:
- 测试会话数据流--验证首次会话和后续会话的数据点和值是否正确
- 确认只有在会话向 Singular 报告后才会收到事件(会话前的事件会产生有机归因
- 确认会话响应已处理并传递到客户端应用程序(对于延迟深度链接至关重要
集成完成:
- 数据收集和存储已验证
- ✓ 已验证到 Singular 的实时流
- 已验证响应处理和日志记录
- 验证所有测试数据流
测试指南:S2S 集成测试指南
高级功能
通过高级归因处理、深度链接、跨设备跟踪和特定平台功能增强 S2S 集成。
苹果搜索广告归因
iOS 搜索广告集成
Apple Search Ads 是一个自归因网络 (SAN),需要通过 Apple 框架实现特定平台的归因功能。
框架支持:
- iOS 14.2 及以下版本: iAd 框架
- iOS 14.3 及以上: AdServices 框架(推荐
双重实施:同时实施 iAd 和 AdServices 框架,直至 iAd 过时。在归因和报告方面,Singular 会优先考虑 AdServices 而非 iAd 信号。
完整指南:苹果搜索广告整合文档
iAd 框架实现
在 iOS 14.2 及以下版本中,通过 iAd 框架检索和发送苹果搜索广告归因数据。
步骤 1:检索归因数据
#import <iAd/iAd.h>
Class ADClientClass = NSClassFromString(@"ADClient");
if (ADClientClass) {
id sharedClient = [ADClientClass performSelector:@selector(sharedClient)];
if ([sharedClient respondsToSelector:@selector(requestAttributionDetailsWithBlock:)]) {
[sharedClient requestAttributionDetailsWithBlock:^(NSDictionary *attributionDetails, NSError *error) {
if (attributionDetails && attributionDetails.count 0) {
// REPORT attributionDetails FROM YOUR APP TO YOUR SERVER
}
}];
}
}
延迟处理:点击搜索广告的用户可能会立即下载并打开应用程序。通过以下方法防止归因时间问题
- 在检索归因数据前设置几秒钟的延迟
- 如果响应错误或错误代码(0、2、3),执行重试逻辑。2 秒后重试
步骤 2:发送到 Singular
通过EVENT 端点以保留名称__iAd_Attribution__ 报告事件,将属性 JSON 作为e 参数传递。
时间要求:
-
iOS 13+:安装/重装后首次会话后立即发送
__iAd_Attribution__事件。否则 Apple Search Ads 数据将不被视为归因数据。 -
iOS 14+:归因响应仅在特定条件下可用,如果 ATT 状态为
ATTrackingManager.AuthorizationStatus.denied则不可用
广告服务框架实现
在 iOS 14.3 及以上版本中,通过 AdServices 框架检索和发送归因令牌。
步骤 1:检索归因令牌
#import <AdServices/AdServices.h>
NSError *error = nil;
Class AAAttributionClass = NSClassFromString(@"AAAttribution");
if (AAAttributionClass) {
NSString *attributionToken = [AAAttributionClass attributionTokenWithError:&error];
if (!error && attributionToken) {
// Handle attributionToken
}
}
令牌特征:
- 在设备上生成
-
缓存 5 分钟--如果调用
attributionToken(),过期后会生成新令牌 - 有效期 24 小时
步骤 2:发送到 Singular
对令牌进行 URL 编码,并在每次安装和重新安装后的首次会话中作为attribution_token 参数追加到SESSION 端点。
谷歌播放安装推荐人
安卓安装归因
Google Play Install Referrer 提供最准确的 Android 安装归属,包含 Play Store 到达之前的用户来源信息。
更多信息:Google Install Referrer 文档
实施步骤:
- 使用Play Install Referrer API在首次打开应用程序时读取安装引用程序
-
通过SESSION 端点报告会话,包括带有所需属性的
install_refJSON 参数
install_ref 属性:
| 属性 | 属性 |
|---|---|
referrer
|
来自 Play Install Referrer API 的 Referrer 值(JSON 对象编码为字符串 |
referrer_source
|
指定 "服务 |
clickTimestampSeconds
|
来自 API 的点击时间戳(例如,"1550420123) |
installBeginTimestampSeconds
|
来自 API 的安装开始时间 |
current_device_time
|
以毫秒为单位的当前设备时间(例如,"1550420454906) |
元安装推荐人
Facebook 归因增强
截至 2025 年 6 月 18 日:Meta 的高级移动测量 (AMM)无需实施 Meta 安装 Referrer。如果启用 AMM,则不建议使用。
Meta Referrer 是针对 Android 的测量解决方案,结合 Google Play Install Referrer 和 Meta Install Referrer 技术,为应用程序的安装提供细粒度的用户级归因数据。
了解更多信息:Meta Referrer 常见问题解答
实施步骤:
- 根据Meta 文档,在首次打开应用程序时检索 Meta 安装推荐器
-
通过SESSION 端点报告会话,包括带有所需属性的
meta_refJSON 参数
meta_ref 属性:
| 属性 | 描述 |
|---|---|
is_ct
|
来自 Meta 安装推荐器的 is_ct(0 或 1) |
install_referrer
|
install_referrer 来自元安装推荐器 |
actual_timestamp
|
来自元安装 Referrer 的 actual_timestamp(例如,1693978124) |
奇异链接和深度链接
对奇异跟踪链接实施深度链接和延迟深度链接支持,以实现从营销活动到特定应用内内容的无缝用户体验。
深度链接是引导用户访问应用程序内特定内容的可点击链接。当用户在安装了应用程序的设备上点击深度链接时,应用程序将打开并显示特定产品或体验。
前提条件
平台配置
客户端应用程序必须将 Singular Links 识别为 iOS 通用链接或 Android 应用程序链接。
配置指南:奇异链接先决条件
实现深度链接
捕获深度链接
当应用程序通过深度链接打开时,通过将 URL 值附加到openuri 参数,捕获并将openURL 添加到 SESSION 请求中。 使用奇异链接时需要。
奇异链接参数:
-
_ios_dl和_android_dl:当链接生成时,每个平台的深层链接值不同,则会出现 -
_dl:当 iOS 和 Android 的深度链接值相同时出现 -
_p:包含直通参数时出现
深度链接处理程序代码
应用程序必须有处理程序代码来解析openURL 并适当处理奇异链接参数。
class DeepLinkHandler {
func handleURL(_ url: URL) {
guard let components = URLComponents(url: url, resolvingAgainstBaseURL: true) else {
return
}
var params: [String: String] = [:]
// Parse query parameters
if let queryItems = components.queryItems {
for item in queryItems {
switch item.name {
case "_dl":
params["deeplink"] = item.value
case "_ios_dl":
params["ios_deeplink"] = item.value
case "_p":
params["passthrough"] = item.value
default:
break
}
}
}
// Handle the parsed parameters
processDeepLinkParameters(params)
}
private func processDeepLinkParameters(_ params: [String: String]) {
// Process the parameters as needed
print("Processed parameters: \(params)")
}
}
// In SceneDelegate or AppDelegate
func scene(_ scene: UIScene, openURLContexts URLContexts: Set<UIOpenURLContext>) {
guard let url = URLContexts.first?.url else { return }
let handler = DeepLinkHandler()
handler.handleURL(url)
}
class DeepLinkHandler {
fun handleDeepLink(intent: Intent) {
val data: Uri? = intent.data
data?.let { uri ->
val params = mutableMapOf<String, String>()
// Parse query parameters
uri.queryParameterNames?.forEach { name ->
when (name) {
"_dl" -> params["deeplink"] = uri.getQueryParameter(name) ?: ""
"_android_dl" -> params["android_deeplink"] = uri.getQueryParameter(name) ?: ""
"_p" -> params["passthrough"] = uri.getQueryParameter(name) ?: ""
}
}
processDeepLinkParameters(params)
}
}
private fun processDeepLinkParameters(params: Map<String, String>) {
// Process the parameters as needed
println("Processed parameters: $params")
}
}
// In your Activity
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
// Handle deep link if activity was launched from a deep link
intent?.let { DeepLinkHandler().handleDeepLink(it) }
}
override fun onNewIntent(intent: Intent?) {
super.onNewIntent(intent)
// Handle deep link if app was already running
intent?.let { DeepLinkHandler().handleDeepLink(it) }
}
}
延迟深度链接
首次安装深度链接
安装后首次打开应用程序时,在向 Singular 报告会话时通过添加参数启用延迟深度链接。
所需参数:
-
install=true -
ddl_enabled=true
Singular 会检查应用程序是否通过带有延迟深度链接的跟踪链接安装。如果是,则返回 SESSION 请求:
-
deferred_deeplink深度链接地址解析:向用户显示正确的产品/体验 -
deferred_passthrough:深层链接中添加的直通参数
响应示例:
{
"deferred_deeplink": "myapp://deferred-deeplink",
"status": "ok",
"deferred_passthrough": "passthroughvalue"
}
动态直通参数
奇异跟踪链接可包含动态直通参数,用于定制用户体验。
如果组织为链接配置了动态直通,则深层链接 URL 包括_p 参数,其中包含 URL 编码的 JSON 字符串值或用于内容/体验路由的非结构化字符串。
更多信息:动态直通参数
短链接解析
当应用程序从缩短的奇异链接打开时,在 SESSION 请求中包含参数,以便将短链接解析为长链接。
必填参数: singular_link_resolve_required=true
Singular 返回未缩短的长链接,以便在链接处理程序中解析深链接和直通参数。
响应示例:
{
"status": "ok",
"resolved_singular_link": "https://myapp.sng.link/A59c0/nha7?_dl=myapp%3A%2F%2Fdeeplink&_ddl=myapp%3A%2F%2Fdeferred-deeplink&_p=passthroughvalue"
}
附加功能
实施跨设备跟踪、收入跟踪、卸载监控和数据隐私合规性,以进行全面分析。
跨设备跟踪
自定义用户 ID 实施
利用custom_user_id 参数将用户与设备级会话关联起来,以便进行跨设备报告和用户级分析。
隐私合规性:在custom_user_id 中避免使用个人身份信息 (PII),从而遵守数据隐私政策。使用散列用户名、电子邮件或随机生成的字符串作为唯一的用户标识符。
实现全面的跨设备报告、用户级数据导出和内部 BI 回传,同时维护用户隐私。
更多信息:自定义用户 ID 参数
收入跟踪
应用内购买报告
跟踪应用内购买的收入,以进行投资回报率分析、营销活动绩效衡量和导出/回传充实。
-
is_revenue_event将事件标记为收入事件(如果事件名称为__iap__或金额 > 0,则跳过该事件 -
purchase_receiptAndroid/iOS:Android/iOS 应用内购买对象--强烈推荐用于交易详情和报告浓缩 -
receipt_signature(安卓):强烈推荐用于交易验证和防止欺诈 -
amt:收入金额为双倍(例如,"amt=1.99) -
cur:ISO 4217 货币代码(例如,"cur=USD)
实施指南:订阅状态管理
卸载跟踪
无声推送通知设置
通过在每个会话通知中发送推送令牌,使用设备静默推送通知跟踪卸载情况。
设置要求:
- 遵循特定平台的卸载跟踪设置:
-
在 SESSION 请求中附加特定平台的令牌:
-
iOS:
apns_token(APNs 设备令牌) -
安卓:
fcm(FCM设备令牌)
-
iOS:
iOS 安装收据
收据验证
报告 iOS 会话时,在 install_receipt
参数。
import Foundation
import StoreKit
class ReceiptManager {
static func getInstallReceipt() - String? {
if #available(iOS 18.0, *) {
let semaphore = DispatchSemaphore(value: 0)
var result: String?
Task {
do {
let transaction = try await AppTransaction.shared
result = transaction.jwsRepresentation
semaphore.signal()
} catch {
debugPrint("Failed to get app transaction: \(error.localizedDescription)")
semaphore.signal()
}
}
semaphore.wait()
return result
} else {
guard let receiptURL = Bundle.main.appStoreReceiptURL else {
debugPrint("Receipt URL not found")
return nil
}
do {
let receiptData = try Data(contentsOf: receiptURL, options: .uncached)
return receiptData.base64EncodedString(options: [])
} catch {
debugPrint("Failed to read receipt: \(error.localizedDescription)")
return nil
}
}
}
}
数据隐私合规性
用户同意处理
通知 Singular 最终用户同意共享数据,以遵守 GDPR、CCPA 和其他隐私法规。
使用 data_sharing_options
参数来传达用户的选择:
-
{"limit_data_sharing":false}用户同意(选择加入)共享信息 -
{"limit_data_sharing":true}用户拒绝共享信息
Singular 在用户隐私回邮(User Privacy Postbacks)中使用limit_data_sharing ,并将信息传递给要求合规的合作伙伴。
可选但建议使用:参数可选,但只有在用户明确选择加入时,合作伙伴才会共享某些属性信息。
更多信息:用户隐私和限制数据共享