OpenDSR API

概要:本文供AppsFlyer客户使用,通常称为广告主或应用所有者,使用该平台记录应用使用情况和归因。应用程序所有者实施 OpenDSR(数据主体请求)API,以遵守适用的数据保护法律,如 GDPR(欧洲)、CCPA(加利福尼亚)、LGPD(巴西)、PDPA(泰国)和 PIPA(韩国)。

来自我们律师的一句话: 本文所述的所有内容仅供参考,不构成法律意见。您应该与法律和其他专业顾问密切合作,以确定GDPR、CCPA或任何其他法律如何适用于您。 您与 AppsFlyer 之间的隐私关系受 AppsFlyer 服务隐私政策的管辖。如果您对我们的服务隐私政策有任何疑问,或需要联系我们的数据保护负责人,请发邮件至privacy@appsflyer.com。为了帮助您遵守《通用数据保护条例》(General Data Protection Regulation)第27条的规定,特此提供AppsFlyer欧盟境内代表人的联系信息:AppsFlyer Germany GmbH, Schönhauser Allee 180, 10119 Berlin, Germany(联系方式:privacy@appsflyer.com;+49-30-166373500)。

隐私监管政策

在本文中,对隐私法规的引用包括以下表格中列出的法规。

规章 Logo 说明
GDPR GDPR.png GDPR :《通用数据保护条例》是有关欧盟公民数据保护和隐私的欧盟法规
CCPA CCPA.png CCPA :加州消费者隐私法 
LGPD LGPD.png Lei Geral de Proteção de Dados
PDPA 6137_Privacy_Shield_Thailand-01.png 个人数据保护法

在本文中,隐私法规、GDPR、CCPA、LGPD 和 PDPA 这几个术语是可以互换使用的。 

开放数据服务资源(OpenDSR)倡议

为了解决和管理数据主体根据隐私法规提交的请求,AppsFlyer 与mParticle、Amplitude和Braze一起启动了OpenGDPR协议。

开放GDPR是一个统一的开源框架, 促进了技术公司之间的合作, 并公正透明地使用消费者数据。该框架使得供应商能够轻松地在多个处理和存储客户数据的系统中执行数据隐私操作。

了解更多关于OpenDSR倡议的信息。

定义

DSR术语 AppsFlyer条款 说明
数据对象 应用用户或终端用户 被收集数据的应用用户
数据控制人 App 所有者/广告主 应用所有者绝定处理个人数据的目的和方式。 
数据处理人 AppsFlyer及其合作伙伴 AppsFlyer及其合作伙伴代表数据控制人处理个人数据

DSR要求

GDPR详细说明了数据控制者必须给予数据主体的强制性权利。出于API目的,这些权利被转换为请求类型。以下详细说明AppsFlyer如何处理不同的请求类型。 

请求类型

(对)

GDPR定义 AppsFlyer处理请求 
进入
  • 如果有要求,App用户有权知道是否,为什么以及应用所有者将处理其数据的时间。
  • 如果与第三方(如AppsFlyer)共享数据,则应用程序用户有权知道这些第三方是谁。
  • 有权知道何种类型的数据正在被使用。
  • 如果有自动处理,这将对他们有重大影响。
应用程序所有者会收到应用程序用户处理过的个人数据的副本。
可移植性 数据对象需要以结构化、常用和机器可读的格式 (如 CSV 文件) 接收其所有个人数据。 应用程序所有者会收到应用程序用户处理过的个人数据的副本。
整改 允许应用程序用户在看到不正确或不真实的数据时更正其数据。应用程序所有者必须删除或更正不准确或不完整的数据。
  • AppsFlyer删除更正请求日期之前记录的App用户数据。
  • 此后收到的数据由AppsFlyer记录。
清除 删除权要求应用所有者在收到请求后的10天内删除个人数据。 数据被删除

AppsFlyer的GDPR请求API

使用本节中描述的GDPR请求API来实现DSR合规性。 

  • GDPR请求 GDPR请求:执行以上请求类型之一:访问权限,可移植性,擦除,纠正。
  • 2. 状态请求查询GDPR请求的当前状态。
  • 4. 发现请求 查询支持的API版本和数据格式。
  • 取消 在待处理阶段取消GDPR请求。

应用程序所有者必须对其应用程序实施用户界面更改,以便应用程序用户可以提交请求。请注意,GDPR请求是针对单个用户的。

1. GDPR请求

GDPR 请求流程

GDPR请求类型,访问权限,可移植性,擦除和纠正具有相同的流程:

  1. 应用程序用户提交请求。
  2. 应用程序所有者构造GDPR请求(请参见下文)并将其发送到AppsFlyer。
  3. AppsFlyer收到请求,并以“ 201 OK”响应有效请求。
  4. 为了访问和可移植性:请求立即得到满足。
    对于删除和更正:
    • 在接下来的48小时内,请求将排队并且处于待处理状态。应用程序用户可以取消请求。 
    • 48小时后,请求状态变为in_progress。AppsFlyer发送状态更改回发。此时,请求无法取消。
    • 在10天内(见下面的说明),AppsFlyer完成请求,请求状态更改为已完成。AppsFlyer发送状态更改回发。

      • 擦除校正的情况下,将删除应用程序用户的数据。
      • 在的情况下, 可移植性的访问, 中应用用户的数据可以在AppsFlyer仪表板下访问 GDPR ,或通过请求API(见下文)。

      请注意:此10天期限从应用所有者提交请求时开始。48小时后,请求状态将变为进行中,并将在额外的8天内完成,总共10天。

GDPR 请求格式

GDPR 请求 API 可以通过 HTTP POST 提交到以下端点-

https://hq1.appsflyer.com/api/gdpr/v1/opendsr_requests

对于API授权,请使用与Pull API相同的V2 API令牌。管理员用户可以从仪表板中的API令牌页面检索令牌。将令牌添加到请求头中,如下所示:

参数

参数名称 必须配置 说明
subject_request_id UUID v4 字符串。由控制器在将请求提交给处理器时生成。之后,可以用它来查询请求状态、更新或取消请求。
subject_request_type 表示 GDPR 请求类型的字符串值。支持的值:
  • 清除
  • 可移植性
  • 进入
  • 整改
subject_identities
  • 定义请求者身份的身份对象数组(请参见下文)。
  • 每个请求只能包含一个主题身份。
submitted_time
  • 数据主体原始请求时间的RFC 3339日期字符串
  • UTC中的时间戳
property_id

代表此请求范围的移动应用程序的字符串:

示例

  • iOS应用:id123456789 
  • Android :com.example,com.publishers.name 
  • 第三方安卓商店 :com.publisher.name-channel
    注意在某些情况下,应用开发者会在Google Play包名的app面板下查看安卓第三方应用商店的归因数据。在这种情况下,请使用面板显示的app name即可。
api_version 表示所需GDPR 请求 API 版本的字符串版本
status_callback_urls 不,但推荐
  • 下列请求状态变更后接收状态回调的端点数组。
  • 仅支持https端点。
  • 无效的URL会被拒绝。
platform

 
价值是支持的DSR平台之一:
可能出现的值为androidioswebwindowsphone

对于CTV、PC或控制台平台请求,请包括以下任何平台: 

 nativepc 
vidaa 
quest

Subject_identities对象

对象类型 必须配置 说明
identity_type
  • 在字符串格式中,以下任一平台的身份类型为: 
    • ios_advertising_id
    • android_advertising_id
    • fire_advertising_id
    • microsoft_advertising_id
    • appsflyer_id
    • Customer user ID(客户用户ID,即CUID) 
  • 仅适用于、和平台的以下内容: 
    • appsflyer_id 
    • Customer user ID(客户用户ID,即CUID)
  • 示例:android_advertising_id

    重要提示!

    要通过 OpenDSR API 使用 CUID,您必须在应用内事件中发送带有 / 的 CUID,使用 : 或

identity_value
  • 格式:字符串
  • 示例:“ a7551968-d5d6-44b2-9831-815ac9017798”
identity_format
  • 仅用于支持对identity_value: rawrawraw进行编码的方法:
  • 示例:“ raw”

举例来说,GDPR 删除请求 

针对 , , , 平台的删除请求示例:

curl --location --request POST 'https://hq1.appsflyer.com/api/gdpr/v1/opendsr_requests' \
--header 'Accept: application/json' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer %AuthTokenv2% \
--data-raw '{ 
 "subject_request_id":"f4e5a271-f25e-4107-b681-************",
 "subject_request_type":"erasure",
 "submitted_time":"2020-07-05T10:00:00Z",
 "platform": "android",
 "subject_identities":[ 
  { 
   "identity_type":"android_advertising_id",
   "identity_value":"55*****-****-****-************",
   "identity_format":"raw"
  }
 ],
 "api_version":"0.1",
 "property_id":"com.*********.*******.********",
 "status_callback_urls":[ 
  "https://examplecontroller.com/opengdpr_callbacks"
 ]
}'
 

针对、和平台的删除请求示例:

curl --location --request POST 'https://hq1.appsflyer.com/api/gdpr/v1/opendsr_requests' \
--header 'Accept: application/json' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer %AuthTokenv2% \
--data-raw '
{"requester": "test@email.com",
"subject_request_id":"valid-uuid4-string",
"subject_request_type":"erasure",
"submitted_time":"2020-07-05T10:00:00Z",
"subject_identities":[ 
 { 
  "identity_type":"appsflyer_id",
  "identity_value":"valid-appsflyer-id-string",
  "identity_format":"raw"
 }
],
"api_version":"0.1",
"property_id":"app-id",
"platform": "roku",
"status_callback_urls":[ 
 "https://examplecontroller.com/opengdpr_callbacks"
 ]
}'

 

GDPR 数据删除请求代码示例

JavaPythonNode.jsC#
/* 
using the okhttp package install from maven
https://mvnrepository.com/artifact/com.squareup.okhttp3/okhttp
 */

import okhttp3.*;
import java.io.IOException;

public class GdprSendRequest {
 public static void main(String[] args){

  OkHttpClient client = new OkHttpClient();
  RequestBody body = RequestBody.create(null, "" +
  "{\r\n\"subject_request_id\": \"\"," +
  "\r\n\"subject_request_type\": \"erasure\"," +
  "\r\n\"platform\": \"android\"," +
  "\r\n\"submitted_time\": \"2018-11-02T15:00:00Z\"," +
  "\r\n\"subject_identities\": [\r\n" +
  "{\r\n\"identity_type\": \"android_advertising_id\"," +
  "\r\n\"identity_value\": \"\"," +
  "\r\n\"identity_format\": \"raw\"\r\n}" +
   "\r\n]," +
   "\r\n\"property_id\": \"com.example.application\"}");

  Request request = new Request.Builder()
  .url("https://hq1.appsflyer.com/api/gdpr/v1/opendsr_requests/opendsr_requests")
  .post(body)
  .addHeader("Content-Type", "application/json")
  .addHeader("Accept", "application/json")
  .addHeader("Authorization: Bearer", "")
  .build();

  try {
   Response response = client.newCall(request).execute();
   System.out.println(response.code());
   System.out.println(response.body().string());
   System.exit(0);
  } catch (IOException e) {
   e.printStackTrace();
   System.exit(1);
  }
 }
}

2. 2. 状态请求

每个已提交的GDPR请求都可以通过subject_request_idsubject_request_idsubject_request_id来查询其后续状态。支持的状态类型有四种:

  1. 待审核-已收到正确的请求, 并且正处于待审核队列中
  2. 正在进行中-正在被执行的请求
  3. 已完成-请求已完成
  4. 已取消-请求已被取消

状态请求格式

状态请求可以通过 HTTP GET 提交到以下端点:

https://hq1.appsflyer.com/api/gdpr/v1/opendsr_requests/<req_id>

状态响应示例

HTTP/1.1 200 OK
Content-Type: application/json
X-OpenGDPR-Processor Domain: example processor.com
X-OpenGDPR-Signature:
kiGlog3PdQx+FQmB8wYwFC1fekbJG7Dm9WdqgmXc9uKkFRSM4uPzylLi7j083461xLZ+mUloo3tpsmyIZpt5eMfgo7ejXPh6lqB4ZgCnN6+1b6Q3NoNcn/+11UOrvmDj772wvg6uIAFzsSVSjMQxRs8LAmHqFO4cF2pbuoPuK2diHOixxLj6+t97q0nZM7u3wmgkwF9EHIo3C6G1SI04/odvyY/VdMZgj3H1fLnz+X5rc42/wU4974u3iBrKgUnv0fcB4YB+L6Q3GsMbmYzuAbe0HpVA17ud/bVoyQZAkrW2yoSy1x4Ts6XKba6pLifIHf446Bubsf5r7x1kg6Eo7B8zur666NyWOYrglkOzU4IYO8ifJFRZZXazOgk7ggn9obEd78GBc3kjKKZdwaCrLx7WV5y9TMDCf+2FILOJM/MwTUy1dLZiaFHhGdzld2AjbjK1CfVzyPssch0iQYYtbR49GhumvkYl11S4oDfu0c3t/xUCZWg0hoR3XL3B7NjcrlrQinB1KbyTNZccKR0F4Lk9fDgwTVkrAg152UqPyzXxpdzXjfkDkSEgAevXQwVJWBNf18bMIEgdH2usF/XauQoyrne7rcMIWBISPgtBPj3mhcrwscjGVsxqJva8KCVCKD/4Axmo9DISib5/7A6uczJxQG2Bcrdj++vQqK2succ=
{
 "controller_id":"example_controller_id",
 "expected_completion_time":"2018-11-01T15:00:01Z",
 "subject_request_id":"a7551968-d5d6-44b2-9831-815ac9017798",
 "request_status":"pending",
}

状态回传

如上GDPR请求流程所述,当GDPR请求状态改变时,从待定进行中完成,AppsFlyer会以status_callback_urls形式给GDPR请求端点回传状态信息。

状态回传示例:

POST /opengdpr_callbacks HTTP/1.1
Host: examplecontroller.com
Content-Type: application/json
X-OpenGDPR-Processor Domain: gdpr.appsflyer.com
X-OpenGDPR-Signature: kiGlog3PdQx+FQmB8wYwFC1fekbJG7Dm9WdqgmXc9uKkFRSM4uPzylLi7j083461xLZ+mUloo3tpsmyIZpt5eMfgo7ejXPh6lqB4ZgCnN6+1b6Q3NoNcn/+11UOrvmDj772wvg6uIAFzsSVSjMQxRs8LAmHqFO4cF2pbuoPuK2diHOixxLj6+t97q0nZM7u3wmgkwF9EHIo3C6G1SI04/odvyY/VdMZgj3H1fLnz+X5rc42/wU4974u3iBrKgUnv0fcB4YB+L6Q3GsMbmYzuAbe0HpVA17ud/bVoyQZAkrW2yoSy1x4Ts6XKba6pLifIHf446Bubsf5r7x1kg6Eo7B8zur666NyWOYrglkOzU4IYO8ifJFRZZXazOgk7ggn9obEd78GBc3kjKKZdwaCrLx7WV5y9TMDCf+2FILOJM/MwTUy1dLZiaFHhGdzld2AjbjK1CfVzyPssch0iQYYtbR49GhumvkYl11S4oDfu0c3t/xUCZWg0hoR3XL3B7NjcrlrQinB1KbyTNZccKR0F4Lk9fDgwTVkrAg152UqPyzXxpdzXjfkDkSEgAevXQwVJWBNf18bMIEgdH2usF/XauQoyrne7rcMIWBISPgtBPj3mhcrwscjGVsxqJva8KCVCKD/4Axmo9DISib5/7A6uczJxQG2Bcrdj++vQqK2succ=

{
"controller_id":"example controller id at the processor",
"expected_completion_time":"2018-11-01T15:00:01Z",
"status_callback_url":"https://examplecontroller.com/opengdpr_callbacks",
"Subject_request_id":"a7551968-d5d6-44b2-9831-815ac9017798",
"Request_status":"pending"
}

验证回调

验证传入回调的真实性:

  1. 创建一个允许列表,列出您允许进行回调的所有处理器域。
  2. 如果头部值在您的允许列表中,请获取证书。
    • 证书 URL 是响应体中 的值。
    • 您还可以直接从 AppsFlyer 端点获取证书,使用 V2 API 令牌作为授权头中的 Bearer 令牌:
  3. 使用库验证证书以确认证书:
    1. 由可信的权威机构发布。
    2. 发放给在头部值中提供的相同字符串。
    3. 没有过期。
  4. 一旦您确认证书有效,请使用它来验证头部与原始请求体的匹配。AppsFlyer使用SHA256 RSA作为签名算法。
  5. 在状态头中获取响应:
    1. 如果验证成功。
    2. 如果签名验证失败或处理器域不在您的允许列表中。

3. 3. 报告请求

一旦访问请求或数据转移请求完成后, 您就可以通过 HTTP GET 将报告下载到以下端点:

https://hq1.appsflyer.com/api/gdpr/v1/download/[REQUEST_ID]

生成的报告自完成之日起七天内有效。

4. 发现请求-处理中

若要了解 AppsFlyer 支持的格式, 可以通过 HTTP GET 将发现请求提交到以下端点:

https://hq1.appsflyer.com/api/gdpr/v1/discovery

发现响应示例

HTTP/1.1 200 OK
Content-Type: application/json
{
 "api_version": "0.1",
 "supported_identities": [
 {
  "identity_type": "android_advertising_id",
  "identity_format": "raw"
 },
 ],
 "supported_subject_request_types": [
 "erasure", "access", "portability", "rectification"
 ],
 "processor_certificate": "https://exampleprocessor.com/cert.pem"
}

5. 5. 取消请求

可以根据其状态取消GDPR请求,但仅在待处理阶段。

要提交一个 HTTP DELETE 请求:

https://hq1.appsflyer.com/api/gdpr/v1/opendsr_requests/<req_id>

取消响应

当收到 GDPR 取消请求时, AppsFlyer 将返回含有状态代码202及其他几个参数的 HTTP 响应。 

一旦请求被取消, AppsFlyer 就会回传其状态为已取消

6. 6. GDPR 请求测试 API

该AppsFlyer API 是 AppsFlyer GDPR 请求 API 的测试 API。

原理

测试 API 的工作原理如下:

1. 1. 一旦提出 GDPR 请求, 该请求将立即处于 "待定" 状态。出于测试目的, 状态每30秒变更一次。

2. 2. 如果在 GDPR 请求中插入了状态回传的端点, 则会在请求之后立即发送第一个回传, 并间隔30秒再发送两次状态回传。

GDPR请求测试端点:POST

https://hq1.appsflyer.com/api/gdpr/v1/stub

状态请求测试端点:GET

https://hq1.appsflyer.com/api/gdpr/v1/stub/:requestId

发现请求测试端点:GET

https://hq1.appsflyer.com/api/gdpr/v1/stub/discovery

取消请求测试端点:删除:

https://hq1.appsflyer.com/api/gdpr/v1/stub/:requestId

证书测试端点:GET

https://hq1.appsflyer.com/api/gdpr/v1/stubcertificate

访问/可移植性报告测试端点GET

https://hq1.appsflyer.com/api/gdpr/v1/stub/download/:requestId

 注意

  • 有效的 V2 API 令牌必须插入到请求头中,如下所示:
  • 在 "property _ id" 中的属性, App ID 必须属于帐户所有者 (根据 API 证书)。

7.请求日志

管理员用户可以访问在日志仪表板中提交的GDPR请求。

对于已完成的访问和数据转移请求, 也可以从该控制面板中下载报表。

访问日志控制面板:

  1. 从页面顶部导航栏打开账户菜单(点击右上角的邮箱地址,打开下拉菜单)> 用户管理
  2. 将打开以下窗口:

GDPR_Table.png

8.GDPR API 返回代码和错误信息

本节详细介绍了GDPR API的HTTP返回代码和错误消息。 

GDPR API 返回代码

响应码 说明
201 已创建
202 取消请求已收到
400 Bad request(请求无效)该主体包含以下表格中列出的错误代码和消息。

HTTP返回代码400 - 错误请求

返回代码为400的消息包含一个带有错误代码和消息的JSON。

{ "error": { "code":400, "af_gdpr_code": "%AF error code%", "message":"%error message text%" } }

返回代码400错误请求消息

错误代码 错误描述(消息)
e111 速率限制超出
e211 无法取消状态无效的请求
e212 请求不被允许。标识符正在进行擦除。
e213 请求已存在
e214 请求未找到
e311 无效的请求内容类型
e312 无效的API版本
e313 无效的 subject_request_id
e314 提交时间格式无效
e315 无效的 status_callback_url 长度
e316 无效的 status_callback_url 格式
e317 无效的 app_id 格式
e318 无效的身份类型
e319 应用程序平台与身份类型不匹配
e320 无效的身份类型
e321 LAT用户不支持通过API
e322 无效的主题请求类型
e323 无效的 subject_identities 格式
e324 无效的 subject_identities 长度
e325 无效的 subject_identities 值
e411 AppID不正确或不属于您的帐户
e412 没有权限取消擦除请求
e413 没有权限查看请求
e511 内部问题,请等待60分钟后再试。如果问题仍然存在,请联系 AppsFlyer 支持。support@AppsFlyer.com 

特点与局限性

  • OpenDSR API 速率限制:每分钟最多可发送350个请求超出此限制将导致错误,需要重新发送请求。在此速率限制内,每天最多允许504,000个请求。
  • 检查来自60天前的请求状态返回“未找到请求”。