Adjust 针对移动端、游戏主机和联网电视 (CTV) 平台应用提供服务器端到服务器端 (S2S) 接口。如果您选择通过 S2S 集成 Adjust,则需要修改应用来实现 Adjust SDK 的相关功能。本指南包含针对下列操作的分步说明:
- 如何对您的应用进行必要的更新。
- 如何向 Adjust 发送 S2S 请求。
操作前须知
以下是您在操作前需要了解的内容。
启用 S2S 会话评估
如果您的移动应用使用的是 S2S 端口,那么 Adjust 需要为您的应用启用 S2S 会话监测。请联系您的 Adjust 代表,或发送邮件至 support@adjust.com 以继续。
当您选择平台时,针对 CTV 监测以及 PC 和主机监测的 S2S 会话监测会自动启用。
设置 S2S 安全
实施 S2S 安全机制,保护您的 S2S 活动不受侵害,防止伪造请求。在 Adjust 控制面板中生成识别码,并在每个传入请求中加入该识别码。
本地的事件队列和保存
用户可能会在设备离线的状态下触发重要事件,如应用安装或会话等。为确保归因准确,您必须在本地捕获并保存这些事件,直至将这些事件成功传输至 Adjust 服务器。
要实装具备持久化功能的本地事件队列,请按照下列步骤操作:
- 创建队列,以便在活动发生时进行保存。
- 为每个活动添加一个以秒为单位的
created_at_unix
时间戳,(例如 1484085154
)代表事件在设备上发生的时间。
- 将此队列保存到本地 (例如 SQLite 数据库或文件形式),以便在应用重新启动时保留。
- 当队列不为空且设备在线时,尝试向 Adjust 服务器发送活动。
- 确保活动成功传送到 Adjust 服务器,然后将活动从队列中移除。
发生下列情况时,上述方法有助于减少数据丢失:
- 短暂的网络连接中断 (例如从 5G 切换到 WiFi 时)。
- 长时间无网络连接。
- 应用在传输前崩溃或强制关闭。
如果没有本地队列,您可能会损失 10% - 20% 的安装数据,严重影响归因的准确性。实装队列系统后,您就能确保 Adjust 完整、准确地呈现用户活动情况,甚至能对离线发生的事件进行精准归因。
添加 iOS 框架
您必须将框架关联到项目,才能支持特定 iOS 功能。要向项目中添加框架,请按照下列步骤操作:
- 使用 XCode 打开您的项目。
- 在项目导航中选择您的目标。
- 前往 General 标签页。
- 滚动并找到 Frameworks, Libraries, and Embedded Content 版块。
- 点击 + 按钮。
- 参考下方表格,搜索并添加应用所需的框架。
框架 | 描述 |
---|
AdSupport.framework | 需要收集 IDFA。以及前 ATT iOS 版本需要收集限制广告跟踪 (LAT) 状态。 |
AppTrackingTransparency.framework | 需要显示 AppTrackingTransparency 弹窗以及 iOS 14.5 及更高版本操作系统中需要收集 IDFA。 |
AdServices.framework | 需要 Adjust 归因 Apple Search Ads 推广活动。 |
StoreKit.framework | 需要投放 SKAdNetwork 推广活动。 |
必要参数
所有 S2S 请求中都必须包含下列参数。
参数 | 描述 |
---|
s2s | 说明该请求属于 S2S 请求。必须硬编码为 1 。 |
app_token | 您的 Adjust 应用识别码。 |
os_name | 移动设备操作系统的名称。请查看下方选项列表。 |
android
android-tv
apple-tv
bada
blackberry
fire-tv
ios
linux
macos
nintendo
playstation
roku-os
server
smart-cast
steamos
symbian
tizen
unknown
webos
windows
windows-phone
xbox
请在您的服务器上创建这些参数。无论使用何种设备,向 Adjust 发出的所有 S2S 请求都会用到这些参数。如有需要,我们还会添加其他设备特定的参数。为简明起见,本指南会从客户端角度展现所有的参数处理活动,但实际上大部分参数处理都在服务器端进行。
// Create dictionary for params to include on all S2S requests to Adjust
var params: [String: String] = [:]
// The name of the operating system running on the device
params["os_name"] = "ios"
// Replace with your Adjust app token
params["app_token"] = "4w565xzmb54d"
// Create dictionary for params to include on all S2S requests to Adjust
NSMutableDictionary *params = [NSMutableDictionary dictionary];
// The name of the operating system running on the device
params[@"os_name"] = @"ios";
// Replace with your Adjust app token
params[@"app_token"] = @"4w565xzmb54d";
// Create map for params to include on all S2S requests to Adjust
val params = mutableMapOf<String, String>()
// The name of the operating system running on the device
params["os_name"] = "android"
// Replace with your Adjust app token
params["app_token"] = "4w565xzmb54d"
// Create map for params to include on all S2S requests to Adjust
Map<String, String> params = new HashMap<>();
// The name of the operating system running on the device
params.put("os_name", "android");
// Replace with your Adjust app token
params.put("app_token", "4w565xzmb54d");
设备 ID 和跟踪状态
每个 S2S 请求都必须包含 至少一个 设备标识符。由于移动操作系统采取的隐私保护措施,广告 ID 有时不可用。因此,您应当在可用的情况下添加广告 ID,并始终在请求中加入备用标识符。
PC 和主机 / CTV 设备 ID
对于 PC、主机和 CTV 监测,您可以随每个回传发送唯一的 external_device_id
参数,以该参数为设备标识符。该参数值可以是任何唯一字符串,只要能够识别设备即可。
iOS 设备 ID
IDFA 和跟踪状态
只有在 iOS 用户授予数据跟踪许可后,您才能读取 iOS 设备的广告主 ID (IDFA)。并非所有应用都会收集 IDFA。但是,无论应用是否收集 IDFA,Adjust 都要求每个请求包含跟踪状态。请根据您的具体实装情况,使用下方对应的代码。
收集 IDFA 的应用
-
在 Xcode 中添加 ATT 描述。
- 打开项目的
Info.plist
文件。
- 在编辑器中右键点击 Information Property List ,然后选择 Add Row 将 key 添加到根。
- 将 key 设置为
NSUserTrackingUsageDescription
。
- 将该值设置为一个字符串,说明请求跟踪许可的原因,例如 “我们将使用该标识符为您投放个性化的广告”。关于如何设计该字符串,请务必仔细阅读 Apple 指南。
-
实装 ATT 弹窗和 IDFA 调取。
ATT 有下列要求:
- iOS 14 首次推出了 ATT 支持,但是只有针对 iOS 14.5 和更高版本操作系统的 IDFA 调取才需要用户许可。因此,Adjust 建议您针对 iOS 14.5 及更高版本的用户显示 ATT 许可请求弹窗。
- 只有在应用处于活跃状态时,ATT 许可请求弹窗才能显示。除非您等待应用状态再次恢复活跃,否则如果在其他系统弹窗出现后立即显示 ATT 许可请求弹窗,可能导致显示失败。
- 该弹窗最早可出现在
applicationDidBecomeActive
(App Delegate) 或 sceneDidBecomeActive
(Scene Delegate) 中。didFinishLaunchingWithOptions
(App Delegate) 中无法显示 ATT 弹窗。
下方示例代码满足上述所有要求。
要获得准确的归因,请在第一个会话请求和后续所有请求中加入可用的 IDFA。
import AppTrackingTransparency
let attStatus: ATTrackingManager.AuthorizationStatus?
let trackingEnabled: Bool?
func getIDFAInfo(completion: @escaping (IDFAInfo) -> Void) {
// For iOS 14.5+, show ATT prompt to attempt to retrieve IDFA and retrieve ATT
if #available(iOS 14.5, *) {
// Wait briefly to ensure app is active
DispatchQueue.main.asyncAfter(deadline: .now() + 1.0) {
ATTrackingManager.requestTrackingAuthorization { status in
let idfa = (status == .authorized) ?
ASIdentifierManager.shared().advertisingIdentifier : nil
completion(IDFAInfo(idfa: idfa, attStatus: status,
// For earlier versions, don't show ATT prompt. Just get IDFA and tracking
let manager = ASIdentifierManager.shared()
let idfa = manager.isAdvertisingTrackingEnabled ?
manager.advertisingIdentifier : nil
let trackingEnabled = manager.isAdvertisingTrackingEnabled
completion(IDFAInfo(idfa: idfa, attStatus: nil,
trackingEnabled: trackingEnabled))
// Include IDFA if available
if let idfa = info.idfa?.uuidString {
// Include ATT status if available,
// otherwise include tracking enabled status, never both
if let attStatus = info.attStatus {
params["att_status"] = String(attStatus.rawValue)
} else if let trackingEnabled = info.trackingEnabled {
params["tracking_enabled"] = trackingEnabled ? "1" : "0"
#import <AppTrackingTransparency/AppTrackingTransparency.h>
#import <AdSupport/AdSupport.h>
@interface IDFAInfo : NSObject
@property (nonatomic, strong) NSUUID *idfa;
@property (nonatomic, assign) ATTrackingManagerAuthorizationStatus attStatus;
@property (nonatomic, strong) NSNumber *trackingEnabled;
void getIDFAInfo(void (^completion)(IDFAInfo *)) {
// For iOS 14.5+, show ATT prompt to attempt to retrieve IDFA and retrieve ATT
if (@available(iOS 14.5, *)) {
// Wait briefly to ensure app is active
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 1 * NSEC_PER_SEC),
dispatch_get_main_queue(), ^{
[ATTrackingManager requestTrackingAuthorizationWithCompletionHandler:
^(ATTrackingManagerAuthorizationStatus status) {
IDFAInfo *info = [[IDFAInfo alloc] init];
if (status == ATTrackingManagerAuthorizationStatusAuthorized) {
info.idfa = [[ASIdentifierManager sharedManager]
// For earlier versions, don't show ATT prompt. Just get IDFA and tracking
ASIdentifierManager *manager = [ASIdentifierManager sharedManager];
IDFAInfo *info = [[IDFAInfo alloc] init];
if (manager.isAdvertisingTrackingEnabled) {
info.idfa = manager.advertisingIdentifier;
info.trackingEnabled = @(manager.isAdvertisingTrackingEnabled);
getIDFAInfo(^(IDFAInfo *info) {
// Include IDFA if available
params[@"idfa"] = [info.idfa UUIDString];
// Include ATT status if available, otherwise tracking enabled status, never both
if (info.attStatus != nil) {
params[@"att_status"] = [NSString stringWithFormat:@"%ld",
} else if (info.trackingEnabled != nil) {
params[@"tracking_enabled"] = [info.trackingEnabled boolValue] ?
不收集 IDFA 的应用
请使用以下代码获取跟踪状态。
let attStatus: ATTrackingManager.AuthorizationStatus?
let trackingEnabled: Bool?
func getTrackingInfo(completion: @escaping (TrackingInfo) -> Void) {
// For iOS 14.5+, get ATT status without showing prompt
if #available(iOS 14.5, *) {
let status = ATTrackingManager.trackingAuthorizationStatus
completion(TrackingInfo(attStatus: status, trackingEnabled: nil))
// For earlier versions, get tracking enabled status
let trackingEnabled = ASIdentifierManager.shared().isAdvertisingTrackingEnabled
completion(TrackingInfo(attStatus: nil, trackingEnabled: trackingEnabled))
getTrackingInfo { info in
// Include ATT status if available, otherwise tracking enabled status, never both
if let attStatus = info.attStatus {
params["att_status"] = String(attStatus.rawValue)
} else if let trackingEnabled = info.trackingEnabled {
params["tracking_enabled"] = trackingEnabled ? "1" : "0"
#import <AdSupport/AdSupport.h>
@interface TrackingInfo : NSObject
@property (nonatomic, assign) ATTrackingManagerAuthorizationStatus attStatus;
@property (nonatomic, strong) NSNumber *trackingEnabled;
@implementation TrackingInfo
void getTrackingInfo(void (^completion)(TrackingInfo *)) {
// For iOS 14.5+, get ATT status without showing prompt
if (@available(iOS 14.5, *)) {
TrackingInfo *info = [[TrackingInfo alloc] init];
info.attStatus = ATTrackingManager.trackingAuthorizationStatus;
// For earlier versions, get tracking enabled status
TrackingInfo *info = [[TrackingInfo alloc] init];
BOOL enabled = [ASIdentifierManager sharedManager].isAdvertisingTrackingEnabled;
info.trackingEnabled = @(enabled);
getTrackingInfo(^(TrackingInfo *info) {
// Include ATT status if available, otherwise tracking enabled status, never both
if (info.attStatus != nil) {
params[@"att_status"] = [NSString stringWithFormat:@"%ld",
} else if (info.trackingEnabled != nil) {
params[@"tracking_enabled"] = [info.trackingEnabled boolValue] ?
IDFV
所有现代 iOS 设备都有备用标识符——供应商 ID (IDFV)。
let idfv: UUID? = UIDevice.current.identifierForVendor
if let idfvString = idfv?.uuidString {
params["idfv"] = idfvString
NSUUID *idfv = [[UIDevice currentDevice] identifierForVendor];
params[@"idfv"] = [idfv UUIDString];
主要去重识别码
要通过卸载和重装,持续一致地监测应用活动,请生成随机版本 4 UUID (即 “主要去重识别码”),并将其保存在 iOS 密钥链中。主要去重识别码是一种备用标识符,您应当为所有设备生成该识别码。
let bundleId = "com.example.app"
// Collect the primary dedupe token from the keychain
func getPrimaryDedupeToken(bundleId: String) -> UUID? {
// Define the query to search for the token in the keychain
let query: [String: Any] = [
kSecClass as String: kSecClassGenericPassword,
kSecAttrAccount as String: "primary_dedupe_token",
kSecAttrService as String: bundleId,
kSecReturnData as String: true
// Attempt to fetch the token from the keychain
let status = SecItemCopyMatching(query as CFDictionary, &item)
// If the fetch was successful, convert the result to a UUID
guard status == errSecSuccess,
let existingItem = item as? Data,
let uuidString = String(data: existingItem, encoding: .utf8),
let token = UUID(uuidString: uuidString) else {
// Return nil if the token doesn't exist or couldn't be collected
// Save the primary dedupe token to the keychain
func setPrimaryDedupeToken(_ token: UUID, bundleId: String) -> Bool {
let tokenData = token.uuidString.data(using: .utf8)!
// Define the attributes for storing the token in the keychain
let query: [String: Any] = [
kSecClass as String: kSecClassGenericPassword,
kSecAttrAccount as String: "primary_dedupe_token",
kSecAttrService as String: bundleId,
kSecValueData as String: tokenData,
kSecAttrAccessible as String: kSecAttrAccessibleAfterFirstUnlock
// Attempt to add the token to the keychain
let status = SecItemAdd(query as CFDictionary, nil)
// Return true if the token was successfully added, false otherwise
return status == errSecSuccess
// Collect the existing primary dedupe token or create a new one if it doesn't exist
func getOrCreatePrimaryDedupeToken() -> UUID {
// Try to collect an existing token
if let existingToken = getPrimaryDedupeToken(bundleId: bundleId) {
// If no token exists, generate a new one
// Attempt to save the new token
if setPrimaryDedupeToken(newToken, bundleId: bundleId) {
// If saving fails, throw a fatal error
fatalError("Failed to save primary dedupe token")
let primaryDedupeToken = getOrCreatePrimaryDedupeToken()
// Convert to lowercase string
params["primary_dedupe_token"] = primaryDedupeToken.uuidString.lowercased()
#import <Foundation/Foundation.h>
#import <Security/Security.h>
NSString *const bundleId = @"com.example.app";
// Collect the primary dedupe token from the keychain
NSUUID *getPrimaryDedupeToken(NSString *bundleId) {
// Define the query to search for the token in the keychain
(__bridge id)kSecClass: (__bridge id)kSecClassGenericPassword,
(__bridge id)kSecAttrAccount: @"primary_dedupe_token",
(__bridge id)kSecAttrService: bundleId,
(__bridge id)kSecReturnData: @YES
// Attempt to fetch the token from the keychain
OSStatus status = SecItemCopyMatching((__bridge CFDictionaryRef)query,
// If the fetch was successful, convert the result to a UUID
if (status == errSecSuccess && item != NULL) {
NSData *existingItem = (__bridge_transfer NSData *)item;
NSString *uuidString = [[NSString alloc]
initWithData:existingItem encoding:NSUTF8StringEncoding];
return [[NSUUID alloc] initWithUUIDString:uuidString];
// Return nil if the token doesn't exist or couldn't be collected
// Save the primary dedupe token to the keychain
BOOL setPrimaryDedupeToken(NSUUID *token, NSString *bundleId) {
NSData *tokenData = [[token UUIDString]
dataUsingEncoding:NSUTF8StringEncoding];
// Define the attributes for storing the token in the keychain
(__bridge id)kSecClass: (__bridge id)kSecClassGenericPassword,
(__bridge id)kSecAttrAccount: @"primary_dedupe_token",
(__bridge id)kSecAttrService: bundleId,
(__bridge id)kSecValueData: tokenData,
(__bridge id)kSecAttrAccessible:
(__bridge id)kSecAttrAccessibleAfterFirstUnlock
// Attempt to add the token to the keychain
OSStatus status = SecItemAdd((__bridge CFDictionaryRef)query, NULL);
// Return YES if the token was successfully added, NO otherwise
return status == errSecSuccess;
// Collect the existing primary dedupe token or create a new one if it doesn't exist
NSUUID *getOrCreatePrimaryDedupeToken(void) {
// Try to collect an existing token
NSUUID *existingToken = getPrimaryDedupeToken(bundleId);
// If no token exists, generate a new one
NSUUID *newToken = [NSUUID UUID];
// Attempt to save the new token
if (setPrimaryDedupeToken(newToken, bundleId)) {
// If saving fails, throw an exception
@throw [NSException exceptionWithName:@"TokenSaveError"
reason:@"Failed to save primary dedupe token"
NSUUID *primaryDedupeToken = getOrCreatePrimaryDedupeToken();
// Convert to lowercase string
params[@"primary_dedupe_token"] = [[primaryDedupeToken UUIDString]
Google Play 设备 ID (Android)
Google 广告 ID
安装 Google Play 服务的 Android 设备可用 Google Play 服务广告 ID (GPS ADID) ,但前提是用户未选择删除其广告 ID。
- 将
play-services-ads-identifier
依赖项添加至应用的 build.gradle
文件:
implementation("com.google.android.gms:play-services-ads-identifier:18.1.0")
implementation 'com.google.android.gms:play-services-ads-identifier:18.1.0'
-
将以下权限添加至您的 AndroidManifest.xml
文件:
<uses-permission android:name="com.google.android.gms.permission.AD_ID"/>
-
如果您的应用使用了 R8 或 ProGuard,请将这些规则添加到您的 proguard-rules.pro
文件,以便在代码优化期间保留 Google 广告 ID 检索所需的类和方法 (如果文件不存在,请在应用模块目录中进行创建):
-keep class com.google.android.gms.common.ConnectionResult {
-keep class com.google.android.gms.ads.identifier.AdvertisingIdClient {
com.google.android.gms.ads.identifier.
AdvertisingIdClient$Info getAdvertisingIdInfo(
android.content.Context);
-keep class com.google.android.gms.ads.identifier.AdvertisingIdClient$Info {
java.lang.String getId();
boolean isLimitAdTrackingEnabled();
-
实装代码,以收集 Google 广告 ID 和跟踪状态:
import com.google.android.gms.ads.identifier.AdvertisingIdClient
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
suspend fun getGoogleAdvertisingIdInfo(
context: Context): AdvertisingIdClient.Info? {
return withContext(Dispatchers.IO) {
AdvertisingIdClient.getAdvertisingIdInfo(context)
// (for example: Google Play Services not available)
// As getGoogleAdvertisingIdInfo is a suspending function,
// it should be called from within a coroutine scope.
val adInfo = getGoogleAdvertisingIdInfo(context)
// Include Google Advertising ID if tracking is not limited
if (!info.isLimitAdTrackingEnabled) {
params["gps_adid"] = info.id
params["tracking_enabled"] = if (info.isLimitAdTrackingEnabled)
App Set ID
App Set ID 是一个备用标识符,适用于所有已安装 Google Play 服务,且运行 API Level 30 (Android 11) 或更高版本的 Android 设备。
- 将必要依赖项添加到应用的
build.gradle
文件:
implementation("com.google.android.gms:play-services-appset:16.1.0")
implementation 'com.google.android.gms:play-services-appset:16.1.0'
- 实装代码以收集 App Set ID:
import com.google.android.gms.appset.AppSet
import com.google.android.gms.appset.AppSetIdClient
import com.google.android.gms.appset.AppSetIdInfo
import com.google.android.gms.tasks.Tasks
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
suspend fun getAppSetId(context: Context): String? {
return withContext(Dispatchers.IO) {
val client: AppSetIdClient = AppSet.getClient(context)
val taskResult = Tasks.await(client.appSetIdInfo)
// Handle exceptions (for example: Google Play Services not available)
// As getAppSetId is a suspending function,
// it should be called from within a coroutine scope.
val appSetId = getAppSetId(context)
val params = mutableMapOf<String, String>()
params["google_app_set_id"] = id
其他参数
这些参数并非必要。如果您使用了其中的任意参数,请在所有请求中添加这些参数。
Unix 时间戳
在每个 S2S 请求中包含 Unix 时间戳,可提供设备上活动发生的时间,提高归因的准确性。
// Unix timestamp of when activity occurred on device
// Code example shows how to retrieve current time in seconds
// Example value: "1484085154"
params["created_at_unix"] = String(Int(Date().timeIntervalSince1970))
// Unix timestamp of when activity occurred on device
// Code example shows how to retrieve current time in seconds
// Example value: "1484085154"
params["created_at_unix"] = (System.currentTimeMillis() / 1000).toString()
概率模型数据点
要使用概率模型作为归因方法,请在所有 S2S 请求中加入下列参数:
参数 | 描述 |
---|
device_name | 设备名称。 |
device_type | 设备类型或型号。 |
os_version | 设备所运行的操作系统版本。 |
ip_address | 设备 IP 地址。 |
Adjust 强烈建议您添加这些参数,这样能获得更全面的归因,对于 iOS 平台尤其如此。
// Example value: "iPhone10,5"
var systemInfo = utsname()
let machineMirror = Mirror(reflecting: systemInfo.machine)
let deviceName = machineMirror.children.reduce("") {
guard let value = element.value as? Int8, value != 0
else { return identifier }
return identifier + String(UnicodeScalar(UInt8(value)))
params["device_name"] = deviceName
// Example value: "iPhone"
params["device_type"] = UIDevice.current.model
// Example value: "17.5.1"
params["os_version"] = UIDevice.current.systemVersion
// Retrieve the device's IP address from requests to your server
params["ip_address"] = "192.0.0.1" // Example value
import android.content.Context
import android.content.res.Configuration
import android.content.pm.PackageManager
val context: Context = // ... get your context here ...
val isGooglePlayGamesForPC = context.packageManager
.hasSystemFeature("com.google.android.play.feature
params["device_name"] = if (isGooglePlayGamesForPC) null
params["os_version"] = if (isGooglePlayGamesForPC) null
else Build.VERSION.RELEASE
params["device_type"] = when {
isGooglePlayGamesForPC -> "pc"
(context.resources.configuration.uiMode and
Configuration.UI_MODE_TYPE_MASK) == Configuration
.UI_MODE_TYPE_TELEVISION -> "tv"
else -> when (context.resources.configuration.screenLayout
and Configuration.SCREENLAYOUT_SIZE_MASK) {
Configuration.SCREENLAYOUT_SIZE_SMALL,
Configuration.SCREENLAYOUT_SIZE_NORMAL -> "phone"
Configuration.SCREENLAYOUT_SIZE_LARGE,
Configuration.SCREENLAYOUT_SIZE_XLARGE -> "tablet"
// Retrieve the device's IP address from requests to
params["ip_address"] = "192.0.0.1" // Example value
环境
您可以传送 environment
参数,说明所发送请求位于怎样的环境。为方便测试,Adjust 会区分来自不同环境的请求。可用的值如下:
sandbox
: 在测试时使用该参数,以区分请求与真实流量。
production
: 在发布应用时使用该参数。
如果不发送此参数,那么默认值为 production
。
// For testing (sandbox environment)
params["environment"] = "sandbox"
params["environment"] = "production"
// For testing (sandbox environment)
params["environment"] = "sandbox"
params["environment"] = "production"
统一回传参数
使用原始数据导出时,您可以在所有 S2S 请求中加入 “统一回传参数”,以便在原始数据中添加自定义参数。这种方法通常用来在原始数据导出中加入您的内部用户 ID。
统一回传参数形式:包含字符串键值对的 JSON 对象。
params["callback_params"] = '{"user_id": "2696775149", "user_category": "high value"}'
params["callback_params"] = '{"user_id": "2696775149", "user_category": "high value"}'
统一合作伙伴参数
在集成特定合作伙伴时,您可能需要在所有 S2S 请求中加入“统一合作伙伴参数”。在向合作伙伴发送的所有回传中,Adjust 服务器都会传递这些参数。如果数据分析合作伙伴需要在收到的回传中包含他们专有的用户 ID,那么经常会使用这种方法。
统一合作伙伴参数形式:包含字符串键值对的 JSON 对象。
params["partner_params"] = '{"analytics_user_id": "3913132433", "analytics_visitor_id": "nzFC9LKSqM"}'
params["partner_params"] = '{"analytics_user_id": "3913132433", "analytics_visitor_id": "nzFC9LKSqM"}'
请求
会话
会话是 Adjust 实现的基础,也是唯一必要的活动。一次会话通常代表一次应用打开。Adjust 服务器会按下列方式记录成功的会话请求:
- 设备首次会话会被记录为 “安装” 活动。
- 后续会话会被记录为 “会话” 活动。
- 如果满足再归因条件,则会被记录 “再归因” 或 “再归因重装” 活动。
在发送带有 created_at_unix
参数的 S2S 会话请求时,Adjust 服务器要求该值比上次成功记录会话的 created_at_unix
时间至少晚 20 分钟。
curl -X POST "https://app.adjust.com/session" \ -H "Authorization: Bearer ADD_YOUR_AUTH_TOKEN_HERE" \
-H "Content-Type: application/x-www-form-urlencoded" \
&idfa=29DDE430-CE81-4F00-A50C-689595AAD142\
&idfv=59E27F41-A86B-4560-B585-63161F871C4B\
&primary_dedupe_token=3b35fcfb-6115-4cff-830f-e32a248c487d\
&created_at_unix=1484085154\
&device_name=iPhone16%2C2\
&callback_params=%7B%22user_id%22%3A%20%222696775149%22%2C%20%22user_category%22%3A%20%22high%20value%22%7D\
&partner_params=%7B%22analytics_user_id%22%3A%20%223913132433%22%2C%20%22analytics_session_id%22%3A%20%22nzFC9LKSqM%22%7D"\
-w "\n\nHTTP Status Code: %{http_code}\n"\
这是 Adjust 成功记录设备首次会话时的响应格式。如有需要,您可以使用 Adjust 测试控制台遗忘设备,并进行多次测试。
"app_token": "4w565xzmb54d",
"adid": "df6c5653080670612cd2f9766ddc0814",
"timestamp": "2024-07-09T01:31:14.373Z+0000",
"message": "Install tracked",
这是 Adjust 成功记录该设备后续会话时的响应格式。
"app_token": "4w565xzmb54d",
"adid": "df6c5653080670612cd2f9766ddc0814",
"timestamp": "2024-07-09T02:31:14.373Z+0000",
"message": "Session tracked",
安装后事件
成功发送至少 1 次设备会话请求后,您就可以发送安装后事件了。这些事件通常代表营销目标,广告渠道可使用这些信息来优化推广活动。
// Add event token to existing params
params["event_token"] = "2y7e81"
// Add revenue and currency, if applicable
// These parameters are equivalent to $19.99
params["revenue"] = "19.99"
params["currency"] = "USD"
// Add event token to existing params
params["event_token"] = "2y7e81"
// Add revenue and currency, if applicable
// These parameters are equivalent to $19.99
params["revenue"] = "19.99"
params["currency"] = "USD"
回传参数
使用原始数据导出时,您可以在特定事件请求中加入自定义 “回传参数”,以添加事件层级自定义数据。例如,如果应用中发生购买事件,您就可以在原始数据中加入该事件的内部交易 ID。
回传参数形式:包含字符串键值对的 JSON 对象。
// If callback_params exists, add the event callback parameters to it (for example: txn_id)
params["callback_params"] = '{"user_id": "2696775149", "user_category": "high value", "txn_id": "8837853376"}'
// If callback_params does not exist, create it
params["callback_params"] = '{"txn_id": "8837853376"}'
// If callback_params exists, add the event callback parameters to it (for example: txn_id)
params["callback_params"] = '{"user_id": "2696775149", "user_category": "high value", "txn_id": "8837853376"}'
// If callback_params does not exist, create it
params["callback_params"] = '{"txn_id": "8837853376"}'
合作伙伴参数
在集成特定合作伙伴时,您可能需要在事件请求中加入自定义 “合作伙伴参数”。在向合作伙伴发送的相关事件回传中,Adjust 服务器会传递这些参数。在启用动态再营销推广活动时,通常会使用这种方法,例如 view_item
、add_to_cart
、购买等事件。
合作伙伴参数形式:包含字符串键值对的 JSON 对象。
// If partner_params exists, add the event partner parameters to it (for example: item_id)
params["partner_params"] = '{"analytics_user_id": "3913132433", "analytics_session_id": "nzFC9LKSqM", "item_id": "[\"76524\",\"62599\"]"}'
// If partner_params does not exist, create it
params["partner_params"] = '{"item_id": "[\"76524\",\"62599\"]"}'
// If partner_params exists, add the event partner parameters to it (for example: item_id)
params["partner_params"] = '{"analytics_user_id": "3913132433", "analytics_session_id": "nzFC9LKSqM", "item_id": "[\"76524\",\"62599\"]"}'
// If partner_params does not exist, create it
params["partner_params"] = '{"item_id": "[\"76524\",\"62599\"]"}'
发送事件请求。
curl -X POST "https://app.adjust.com/event" \
-H "Authorization:Bearer ADD_YOUR_AUTH_TOKEN_HERE" \
-H "Content-Type:application/x-www-form-urlencoded" \
&idfa=29DDE430-CE81-4F00-A50C-689595AAD142\
&idfv=59E27F41-A86B-4560-B585-63161F871C4B\
&primary_dedupe_token=3b35fcfb-6115-4cff-830f-e32a248c487d\
&created_at_unix=1484085154\
&device_name=iPhone16%2C2\
&callback_params=%7B%22user_id%22%3A%20%222696775149%22%2C%20%22user_category%22%3A%20%22high%20value%22%2C%20%22txn_id%22%3A%20%228837853376%22%7D\
&partner_params=%7B%22analytics_user_id%22%3A%20%223913132433%22%2C%20%22analytics_session_id%22%3A%20%22nzFC9LKSqM%22%2C%20%22item_id%22%3A%20%22%5B%5C%2276524%5C%22%2C%5C%2262599%5C%22%5D%22%7D"\
-w "\n\nHTTP Status Code: %{http_code}\n"\