简介 :移动端的服务器到服务器(S2S)事件API将应用程序外部发生的事件发送到AppsFlyer。S2S事件的归因类似于SDK事件,并且可适用于整个平台。
适用于移动设备的服务器到服务器事件API
AppsFlyer平台对由AppsFlyer SDK和API发送的移动应用程序事件进行归因和记录。使用S2S API报告应用程序外部发生的事件;例如,用户使用您的Web界面续订订阅。一旦记录了S2S事件,就可以在整个平台上使用它,包括控制面板,原始数据和分析报告。有关PBA Web事件,请参阅Web S2S for PBA。
AppsFlyer将以下信息填充到S2S事件:
- S2S消息中发送的值
- 某些AppsFlyer激活归因的值,例如激活时间和媒体渠道。
API使用说明
使用以下各节中包含的信息创建您的API调用。
S2S API事实
API节点(endpoint) |
|
HTTP方法 | POST |
接受的内容类型 | application/ json |
授权 |
|
服务器获取响应消息允许列表 | |
JSON有效负载限制 |
JSON有效负载大小:最大1KB |
拉取频次限制 |
POST限制数量:每分钟60,000 POST。要增加此限制,请联系您的CSM。 |
编码说明 |
|
编码URLs |
将所有的保留字符进行URL编码(百分比编码)处理 (https://tools.ietf.org/html/rfc3986#section-2.1 )在最终生成URL之前。 |
TLS |
使用TLS 1.2或更高版本。支持的密码 |
有效负载参数
- Payload parameters consist of one or more device identifiers (depending on the operating system).
-
What if I can't send a device identifier?
- You may be unable to send the identifier for a reason out of your control, for example, because the user has limited ad tracking (LAT) or uses iOS 14, and did not give ATT consent. If IDFV is available send it.
- Not sending an advertising ID/device identifier can cause:
- Postback issues: The media source will receive the postback but without a device identifier; consequently the media source can't associate it with a user.
- Audiences segmentation and rule failure. Audiences rulesets require identifiers. Send a device ID or customer user ID according to the ID type your ruleset uses.
操作系统 | 标识符名称 | 描述 |
---|---|---|
iOS
|
idfa |
如可获取,此处填充设备IDFA 格式:字符串 例如: |
idfv |
如可获取,此处填充设备IDFA Format: String |
|
Android |
advertising_id |
在可用的地方填充设备GAID(广告ID) 格式:字符串 示例: |
oaid |
格式:字符串 例如: |
|
amazon_aid |
格式:字符串 |
|
imei |
格式:字符串 例如: |
参数名称 | 强制性的内容 | 描述 | |||||||||
---|---|---|---|---|---|---|---|---|---|---|---|
appsflyer_id |
是 |
应用程序首次启动时由AppsFlyer生成的唯一标识符。
|
|||||||||
CUSTOMER_USER_ID |
否 |
客户用户ID(CUID), 应用所有者设置的唯一用户标识符。
|
|||||||||
att |
否 |
注意! As part of Apple privacy policies, we recommend that you populate |
|||||||||
ip |
否 |
事件发生期间的移动设备的IP地址。
|
|||||||||
弃用 |
|
||||||||||
eventName |
是 |
指定事件名称。确保事件名称与营销商的要求一致。
|
|||||||||
Event value |
是 |
If you send an event without a value then send:
|
|||||||||
app_version_name |
否 |
您的应用版本或标识符。
|
|||||||||
app_store |
否 |
Equivalent to AF_STORE in Android apps. The store from which the app was downloaded.
|
|||||||||
事件时间 |
否 |
事件发生的时间使用UTC时区。
一天结束:
示例
|
|||||||||
eventCurrency |
否 |
货币代码使用ISO 4217 3-字符代码和BCN(比特币)
|
|||||||||
bundleIdentifier |
No* |
A unique app identifier. In raw-data, the parameter populates Bundle ID. [Best practice] Always populate this parameter. Many ad networks require it for campaign optimization.
|
|||||||||
sharing_filter |
否 |
sharing filter将阻止与合作伙伴和其他第三方通过回调/ API共享S2S事件。 使用过滤器可满足GDPR和CCPA等法规要求,遵守用户选择退出机制以及其他业务逻辑。 Sharing_filter具有以下选项:
合作伙伴ID的列表,请联系您的CSM或AppsFlyer技术支持。 |
|||||||||
custom_dimension |
否 |
Reserved for AppsFlyer future use |
|||||||||
app_type |
否 |
For iOS apps. Permitted value: If the user event takes place in an app_clip, send the parameter. In all other cases don't send the parameter. |
|||||||||
custom_data |
否 |
Send custom data to the AppsFlyer platform. Similar to sending data from the SDK using In raw data: populates Format: JSON, illustrated in the example that follows. Example:
|
|||||||||
* Required by many ad networks for optimization purposes |
Curl example
curl --location --request POST 'https://api2.appsflyer.com/inappevent/<app_id_placeholder>'\
--header 'authentication: <dev_key_placeholder>
' \
--header 'Content-Type: application/json' \
--data-raw '{
"appsflyer_id": "9999999999999-9999999999999999999",
"advertising_id": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa",
"customer_user_id" : "example_customer_id_123",
"ip": "199.0.2.1",
"app_version_name" : "example_version_name",
"eventTime" : "2020-02-25 12:00.000",
"eventName": "af_purchase",
"eventCurrency": "ZAR",
"eventValue":
"{
\"af_revenue\": \"1006\",
\"af_content_type\": \"wallets\",
\"af_content_id\": \"15854\",
\"af_quantity\" :\"1\"
}"
}
'
响应代码
响应代码 | 信息 | 如何处理 |
---|---|---|
200 | OK |
收到消息后,将执行最小数据验证。这样,即使事件可能未完全记录在AppsFlyer中,您也可以获得OK响应。调试(debug)事件:
|
400 | 无法进行身份验证 | 确保身份验证密钥(authentication key)正确。 |
400 | appsflyer_id 是必填字段 |
|
401 | 未经授权 | authentication header中提供的密钥不是此应用程序的开发者秘钥<dev_key>。 |
400 | 错误的请求 |
至少要有一项验证标准请求失败。 |
400 | 有效载荷丢失或解析失败 |
|
500 | 内部服务器错误 | 验证JSON是否为stringified()且格式正确。 |
测试
考虑:
- 对于测试,请使用非自然激活的AppsFlyer ID,以便测试事件能实时进行归因。自然事件的归因会延迟几个小时。
- 事件的某些字段是根据用户的激活归因事件填充的。例如,激活时间和媒体渠道。
要将测试设备归为非自然激活:
- 准备自定义归因链接,用于 S2S 消息测试。将链接上的媒体渠道参数设置为s 2s_test 。示例:
&pid=s2s_test
- 注册测试设备
- 从测试设备上卸载应用程序。
- 将链接发送到测试设备。
- 点击链接
- 安装并启动该应用程序。
- 在面板上,检查激活是否被归因了。
- 提取AppsFlyer ID 以在您的S2S消息中使用。
发送S2S测试消息 :
- 使用分配的AppsFlyer ID准备S2S消息。有关代码示例,请参见以下内容。
- 发送消息。
- 请执行以下一项操作,以查看AppsFlyer如何记录该消息:
- Download the in-app events raw data report. Allow up to 15 minutes after sending the event for it to show up in the raw data report.
- Push API (考虑使用 Postman 和 Webhook网站之类的工具。)
- 检查并查看:
- S2S消息中发送的值正确填充了报告。请特别注意“事件日期”,“事件币种”,“事件收入”和“事件值”字段。
- 归因渠道和激活时间由AppsFlyer填充。
- SDK本身提供的值(例如SDK版本)不会被填充。
发送S2S消息的示例代码
/* using the okhttp package
install from maven https://mvnrepository.com/artifact/com.squareup.okhttp3/okhttp */
import okhttp3.*;
import java.io.IOException;
public class SendRequest {
public static void main(String[] args) {
OkHttpClient client = new OkHttpClient();
MediaType mediaType = MediaType.parse("application/json");
RequestBody body = RequestBody.create(mediaType, "" +
"{\r\n\t\"appsflyer_id\": \"<APPS_FLYER_ID>\"," +
"\r\n\t\"customer_user_id\": \"123456\",\r\n\t\"eventName\": \"af_purchase\",\r\n\t\"" +
"eventValue\": \"{\\\"af_revenue\\\":\\\"6\\\" ,\\\"af_content_id\\\":\\\"15854\\\"}\",\r\n\t\"" +
"eventCurrency\": \"USD\",\r\n\t\"" +
"eventTime\": \"2018-08-10 4:17:00.000\",\r\n\t\"");
Request request = new Request.Builder()
.url("https://api2.appsflyer.com/inappevent/<APP_ID>")
.post(body)
.addHeader("Content-Type", "application/json")
.addHeader("authentication", "<YOUR_DEV_KEY>")
.build();
try {
Response response = client.newCall(request).execute();
System.out.println(response.code());
System.out.println(response.body().string());
} catch (IOException e) {
e.printStackTrace();
}
}
}
''' using the requests python package, install using pip install requests '''
import requests
url = "https://api2.appsflyer.com/inappevent/[Insertapp ID here]"
payload = "{\r\n \"appsflyer_id\": \"9999999999999999999999\",\r\n\t\"IDFA\":\"999999999999999999999999\",\r\n\t\"customer_user_id\" : \"14mar\",\r\n\t\"ip\": \"10.0.0.1\",\r\n\t\"app_version_name\" : \"example_version_name\",\r\n\t\"eventTime\" : \"2020-04-25 08:59:01.23\",\r\n\t\"eventName\":\"gaf_purchase\",\r\n\t\"eventCurrency\": \"ZAR\",\r\n\t\"eventValue\": \r\n\t\"{\r\n\t\t\\\"af_revenue\\\": \\\"1000\\\",\r\n\t\t\\\"af_content_type\\\": \\\"wallets\\\",\r\n\t\t\\\"af_content_id\\\": \\\"15854\\\",\r\n\t\t\\\"af_quantity\\\" :\\\"1\\\"\r\n }\"\r\n}"
headers = {
'authentication': '[Insert web dev key]',
'Content-Type': 'application/json'
}
response = requests.request("POST",url, headers=headers, data = payload)
print(response.text.encode('utf8'))
/* using the request npm package, install using npm install request */
var request = require("request");
var options = { method: 'POST',
url: 'https://api2.appsflyer.com/inappevent/<APP_ID>',
headers:
{
"authentication": '<YOUR_DEV_KEY>',
'Content-Type': 'application/json'
},
body:
{ appsflyer_id: '<APPS_FLYER_ID>',
customer_user_id: '123456',
eventName: 'node_js',
eventValue: '{"node_js":"6" ,"af_content_id":"15854"}',
eventCurrency: 'USD',
ip: '1.0.0.0',
eventTime: '2018-07-09 4:17:00.000'
},
json: true };
request(options, function (error, response, body) {
if (error) throw new Error(error);
console.log(body);
});
/* using the RestSharp package, install using NuGet */
using System;
using RestSharp;
namespace CS
{
class Event
{
static void Main(string[] args)
{
var client = new RestClient("https://api2.appsflyer.com/inappevent/<APP_ID>");
var request = new RestRequest(Method.POST);
request.AddHeader("authentication", "<YOUR_DEV_KEY>");
request.AddHeader("Content-Type", "application/json");
var body = "{\"appsflyer_id\": \"<APPS_FLYER_ID>\"," +
"\"customer_user_id\": \"123456\"," +
"\"eventName\": \"af_purchase\"," +
"\"eventValue\": \"{\\\"af_revenue\\\":\\\"6\\\" ,\\\"af_content_id\\\":\\\"15854\\\"}\"," +
"\"eventCurrency\": \"USD\"," +
"\"eventTime\": \"2018-07-08 4:17:00.000\"
}";
request.AddParameter("undefined", body, ParameterType.RequestBody);
IRestResponse response = client.Execute(request);
// handle response by reading response.StatusCode
Console.WriteLine(response.Content);
}
}
}
$purchase_event = array(
'appsflyer_id' => <APPS_FLYER_DEVICE_ID>,
'idfa' => <IDFA>,
'eventCurrency' => <PURCHASE_CURRENCY>,
'ip' => <DEVICE_ID_ADDRESS>,
'eventTime' => date("Y-m-d H:i:s.000", time())
);
$purchase_event['eventName'] = 'af_purchase';
$purchase_event['eventValue'] = json_encode(array('af_revenue' => <PURCHASE_REVENUE>,
'af_price' => <PURCHASE_PRICE>,
'af_order_id' => <PURCHASE_ORDER_ID>,
'af_currency' => <PURCHASE_CURRENCY>,
'af_content_type' => <PURCHASE_TYPE>,
'af_quantity' => <PURCHASE_QUANTITY>,
'af_content' => <PRODUCT_NAME>,
'af_content_id' => <PRODUCT_ID>)
);
$data_string = json_encode($purchase_event);
$ch = curl_init('https://api2.appsflyer.com/inappevent/<YOUR_APP_ID>');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "POST");
curl_setopt($ch, CURLOPT_HEADER, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, $data_string);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, array(
'Content-Type: application/json',
'authentication: <YOUR_APPS_FLYER_DEV_TOKEN>',
'Content-Length: ' . strlen($data_string))
);
$result = curl_exec($ch);
$curl = curl_init();
AppsFlyer Go script on Github
发送advertising ID /设备标识符很重要
- Advertising ID /设备标识符是必填项,以确保可以发送回调数据到Facebook和Google Ads等SRN。如果您无法发送该ID,请考虑我们将无法发送回调数据。
- 如果仅发送AppsFlyer ID,则应用内事件将被记录并正确归因。
填充参数
自然量与非自然量的区别
当AppsFlyer处理S2S应用内事件时,将使用AppsFlyer ID来识别关联应用内事件之前的激活事件来填充归因字段。
这意味着 AppsFlyer 将某些数据与非自然 S2S 应用内事件相关联, 但与自然S2S 应用内事件无关。
示例
例如, 如果比较非自然和自然 S2S 应用内事件的原始数据报告, 则非自然事件会包含自然应用内事件所不包含的数据。
非自然应用内事件包括的数据有媒体渠道、广告系列、归因接触类型、归因接触时间等。
另一方面, 自然应用内事件来自于自然激活。自然激活没有相关的广告系列,媒体渠道,归因接触类型和激活时间等相关的数据。
将AppsFlyer ID与客户用户ID(CUID)映射
需要后端逻辑来获取值以填充参数。下面介绍如何获取AppsFlyer ID:
- AppsFlyer ID是必要的,用于归因事件。
- 它是在用户首次激活移动应用程序时生成的。
- 所以您可以将CUID映射到AppsFlyer ID,您需要在应用程序中设置CUID。
为了使您更容易了解哪个用户执行了哪个事件,请执行以下流程:
- 当用户激活应用的时候设置客户用户 ID(CUID)
- AppsFlyer原始数据报告包含CUID和AppsFlyer ID。使用数据传递工具之一来获取或使用AppsFlyer Push API。
- 使用原始数据报告将CUID与AppsFlyer ID匹配。
- AppsFlyer ID在集成到您的应用程序( Android / iOS )中的SDK中可用。
- 将AppsFlyer ID映射到内部系统中的客户用户ID(CUID)(对于将来使用很重要)。
将AppsFlyer ID与CUID映射后,即可将用户与执行的事件进行匹配。然后,您可以获取其他值(事件值,事件币种,事件时间等),并发送服务器到服务器应用内事件。
请求正文示例
{
"appsflyer_id": "1415211453000-6513894",
"advertising_id": "38412345-8cf0-aa78-b23e-10b96e40000d",
"eventName": "af_purchase",
"eventValue":
"{
\"af_revenue\": \"6\",
\"af_content_type\": \"wallets\",
\"af_content_id\": \"15854\",
\"af_quantity\" :\"1\"
}",
"eventCurrency": "USD",
"ip": "1.2.3.4",
"eventTime": "2014-05-15 12:17:00.000"
}
In this case, AppsFlyer receives an S2S in-app event which represents a purchase event with revenue, as well as additional properties such as content type, etc.
提取AppsFlyer ID
发送负收入
可以发送收入值为负的事件。例如,如果购买被取消。参数af_revenue
可以用负值来记录此信息。
如果您填充af_quantity
,则可能根据你们的系统逻辑使用负值填充它。AppsFlyer不使用af_quantity
。
负收入示例
{
“ eventName”:“ cancel_purchase”,
“ eventValue”:
“ {
\"af_revenue\": \"-6\",
\"af_content_type\": \"wallets\",
\"af_content_id\": \"15854\",
\"af_quantity\" :\"1\"
} ”,
“ eventCurrency”:“ USD”,
}
发送没有事件值的事件
If you want to send events without event value, simply pass an empty string to event value: "eventValue":""
AppsFlyer 将根据广告主的配置,向媒体渠道发送此类详细应用内事件,实现高级定向、优化和受众创建。
疑难解答
事件未显示在面板上
事件不包含收入
如果发送S2S事件,但未记录其收入:请确保对发送的JSON进行了字符串化。最重要的部分是JSON中的事件值参数。它必须按照下面的示例所示进行字符串化。
"{\"af_revenue\":\"6\" ,\"af_content_type\":\"wallets\"}"
如果未进行字符串化,则将无法正确处理事件值,也不会记录收入。
收入值不得以任何方式设置格式。它们可以包含十进制逗号,但这不是强制性的。不包括货币符号或代码也不包括,
分隔符。收入可以以-
为前缀
- 有效值示例为:
123
,-123.45
,123.456
- 非法值示例:
1,234.56
,1,234
S2S事件中并非所有字段都要填充
原始数据的字段即使用S2S调用中发送的值进行填充,也使用激活事件的某些字段进行填充。对于使用AppsFlyer SDK上报的应用内事件,可以观察到类似的但并不完全相同的行为。还有一些特定的不同之处,S2S事件不会填充以下字段:
- WIFI
- operator
- language
- 设备型号
- 设备类别
- App Version: You can use
app_version_name
- 应用名称