This repository has been archived by the owner on Sep 22, 2023. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 68
/
user-privacy-api.md
587 lines (464 loc) · 17.1 KB
/
user-privacy-api.md
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
---
title: User Privacy API
description: The User Privacy API lets you programmatically submit requests to delete all data for a set of known Amplitude IDs or User IDs.
---
The User Privacy API helps you comply with end-user data deletion requests mandated by global privacy laws such as GDPR and CCPA.
The API lets you programmatically submit requests to delete all data for a set of known Amplitude IDs or User IDs.
--8<-- "includes/postman.md"
--8<-- "includes/auth-basic.md"
## Endpoints
| Region | Endpoint |
| --- | --- |
| Standard Server | [https://amplitude.com/api/2/deletions/users](https://amplitude.com/api/2/deletions/users) |
| EU Residency Server | [https://analytics.eu.amplitude.com/api/2/deletions/users](https://analytics.eu.amplitude.com/api/2/deletions/users) |
## Considerations
Keep these considerations in mind when using the User Privacy API.
- When you make a deletion request, Amplitude [emails all account admins](https://help.amplitude.com/hc/en-us/articles/360031965572-Manage-user-privacy-notifications-in-Amplitude) with the deletion details.
- Amplitude deletes all events and user properties added up until the time that job runs for each Amplitude ID in a deletion job.
- Running a deletion job for a user doesn't block new events for that user. Amplitude accepts new events from a deleted user.
- If Amplitude receives events for a deleted user, then it counts the deleted user as a new user.
Because deletion removes all user data from Amplitude servers, Amplitude doesn't recognize the new user as the deleted user.
- To reduce resource impact and ensure high availability, Amplitude batches requests for deletions. Batch jobs are scheduled for 10-13 days after the date of the batch's first request.
In line with GDPR article 12.3 and 17, Amplitude processes deletion requests without undue delay, within 30 days after receiving the request.
The actual timeline for deletion depends on the complexity and number of requests Amplitude receives.
If your data volume is large (>1BB/month), then Amplitude may need to reduce your frequency of deletion scheduling.
- You can revoke requests in the batch until three (3) days before the day the job scheduled run date. During the three (3) day period, you can't edit the batch.
Amplitude adds deletion requests made during this time to a new batch.
- After the three (3) day period, the request's status changes to `submitted`. You can't stop the job at this point.
The deletion process removes all data associated with the user from all Amplitude's systems, including associated recovery and back-up systems.
After the job completes, its status changes to `done`.
!!! warning "User tracking"
Using this API doesn't prevent future user tracking for the deleted users. To learn about how to stop tracking users in your application, see the `setOptOut()` method in documentation for the Amplitude SDK you're using.
## Limits
The endpoint `/api/2/deletions/users` has a rate limit of 1 HTTP request per second. Each HTTP request can contain up to 100 `amplitude_ids` or `user_ids`.
You can make up to 100 deletion requests per second if you batch 100 users in each request.
## Delete users
`POST /deletions/users`
Add a user for deletion using a JSON body. Specify up to 100 users at a time. You can use mix of Amplitude IDs and User IDs.
### JSON body parameter
The body parameter is required. It's the deletion request object listing the `user_ids` and `amplitude_ids` for the users to delete.
| <div class="big-column">Name</div> | Description |
| --- | --- |
| `amplitude_ids` | Amplitude IDs for the users to delete. |
| `user_ids` | User IDs for the users to delete. |
| `requester` | The internal user who requested the deletion. This is useful for auditing. |
| `ignore_invalid_id` | When `true`, the job ignores invalid user IDs. Invalid user IDs are users that don't exist in the project. |
| `delete_from_org` | Delete user from the entire org instead of a single project. This feature is available in orgs with the Portfolio feature enabled. Requests must be by `user_ids`. Values can be either `True` or `False`. Defaults to `False`. |
| `include_mapped_user_ids` | When `true`, each valid `user_id` from `user_ids` is included in a corresponding object of `amplitude_ids` response array. |
### Example request
=== "cURL"
```bash
curl --location --request POST 'https://amplitude.com/api/2/deletions/users' \
--header 'Authorization: Basic {{api-key}}:{{secret-key}} \ # credentials must be base64-encoded
--header 'Content-Type: application/json' \
--data-raw '{
"amplitude_ids": [
356896327775,
356896327755
],
"user_ids": [
1000,
2999
],
"ignore_invalid_id": "true",
"delete_from_org": "false",
"requester": "employee@yourcompany.com"
}'
```
=== "HTTP"
```bash
POST /api/2/deletions/users HTTP/1.1
Host: amplitude.com
Authorization: Basic {{api-key}}:{{secret-key}} # credentials must be base64-encoded
Content-Type: application/json
Content-Length: 238
{
"amplitude_ids": [
356896327775,
356896327755
],
"user_ids": [
1000,
2999
],
"ignore_invalid_id": "true",
"delete_from_org": "false",
"requester": "employee@yourcompany.com"
}
```
=== "JavaScript"
```js
var headers = {
'Content-Type':'application/json',
'Accept':'application/json'
};
$.ajax({
url: 'https://amplitude.com/api/2/deletions/users',
method: 'post',
headers: headers,
success: function(data) {
console.log(JSON.stringify(data));
}
})
```
=== "NodeJs"
```js
const request = require('node-fetch');
const inputBody = '{
"amplitude_ids": [
"amp_id_1",
"amp_id_2",
"..."
],
"user_ids": [
"user_id_1",
"user_id_2",
"..."
],
"requester": "employee@yourcompany.com"
}';
const headers = {
'Content-Type':'application/json',
'Accept':'application/json'
};
fetch('https://amplitude.com/api/2/deletions/users',
{
method: 'POST',
body: inputBody,
headers: headers
})
.then(function(res) {
return res.json();
}).then(function(body) {
console.log(body);
});
```
=== "Ruby"
```ruby
require 'rest-client'
require 'json'
headers = {
'Content-Type' => 'application/json',
'Accept' => 'application/json'
}
result = RestClient.post 'https://amplitude.com/api/2/deletions/users',
params: {
}, headers: headers
p JSON.parse(result)
```
=== "Python"
```python
import requests
import json
url = "https://amplitude.com/api/2/deletions/users"
payload = json.dumps({
"amplitude_ids": [
356896327775,
356896327755
],
"user_ids": [
1000,
2999
],
"ignore_invalid_id": "true",
"delete_from_org": "false",
"requester": "employee@yourcompany.com"
})
headers = {
'Authorization': 'Basic {{api-key}}:{{secret-key}}', #credentials must be base64-encoded
'Content-Type': 'application/json'
}
response = requests.request("POST", url, headers=headers, data=payload)
print(response.text)
```
=== "Java"
```java
URL obj = new URL("https://amplitude.com/api/2/deletions/users");
HttpURLConnection con = (HttpURLConnection) obj.openConnection();
con.setRequestMethod("POST");
int responseCode = con.getResponseCode();
BufferedReader in = new BufferedReader(
new InputStreamReader(con.getInputStream()));
String inputLine;
StringBuffer response = new StringBuffer();
while ((inputLine = in.readLine()) != null) {
response.append(inputLine);
}
in.close();
System.out.println(response.toString());
```
=== "Go"
```go
package main
import (
"fmt"
"strings"
"net/http"
"io/ioutil"
)
func main() {
url := "https://amplitude.com/api/2/deletions/users"
method := "POST"
payload := strings.NewReader(`{
"amplitude_ids": [
356896327775,
356896327755
],
"user_ids": [
1000,
2999
],
"ignore_invalid_id": "true",
"delete_from_org": "false",
"requester": "employee@yourcompany.com"
}`)
client := &http.Client {
}
req, err := http.NewRequest(method, url, payload)
if err != nil {
fmt.Println(err)
return
}
req.Header.Add("Authorization", "Basic {{api-key}}:{{secret-key}}") // credentials must be base64-encoded
req.Header.Add("Content-Type", "application/json")
res, err := client.Do(req)
if err != nil {
fmt.Println(err)
return
}
defer res.Body.Close()
body, err := ioutil.ReadAll(res.Body)
if err != nil {
fmt.Println(err)
return
}
fmt.Println(string(body))
}
```
### Response
The response for a POST request contains these fields:
| <div class="big-column">Name</div> | Description |
| --- | --- |
| `day` | The day the deletion job is scheduled to begin. |
| `status` | The status of the deletion job. |
| `amplitude_ids` and `user_ids` | List of the Amplitude IDs to delete. |
| `app` | The project or app ID. Included when the deletion request is for multiple projects. |
The `amplitude_ids` key contains these fields:
| <div class="big-column">Name</div> | Description |
| --- | --- |
| `amplitude_id` | The Amplitude ID of the user to be deleted. |
| `requester` | The person who requested the Amplitude ID to be deleted. |
| `requested_on_day` | The day this deletion was requested. |
| `user_id` | The corresponding User ID. Included when `include_mapped_user_ids` is `true` and the `amplitude_id` is mapped from one of `user_ids`. |
## Get deletion jobs
`/api/2/deletions/users?start_day=YYYY-MM-DD&end_day=YYYY-MM-DD`
Retrieves a list of deletion jobs scheduled in a time range. The time range should include the date you made the request on plus 30 days. For example, you made a deletion request on August 1st, 2018.
Your deletion request should have `start_day = 2018-08-01` and `end_day = 2018-08-31`.
If the request returns no values, then no jobs are scheduled for that time range. Note: The largest permitted time range is six months.
### Example request
<!--vale off-->
=== "cURL"
```bash
# You can also use wget
curl -X GET https://amplitude.com/api/2/deletions/users?start_day=string&end_day=string \
-H 'Accept: application/json' \
-U API_Key:API_Secret # credentials must be base64-encoded
```
=== "HTTP"
```bash
GET https://amplitude.com/api/2/deletions/users?start_day=string&end_day=string HTTP/1.1
Host: amplitude.com
Authorization: Basic {{api-key}}:{{secret_key}} # credentials must be base64-encoded
Accept: application/json
```
=== "JavaScript"
```js
var headers = {
'Accept':'application/json'
};
$.ajax({
url: 'https://amplitude.com/api/2/deletions/users',
method: 'get',
data: '?start_day=string&end_day=string',
headers: headers,
success: function(data) {
console.log(JSON.stringify(data));
}
})
```
=== "NodeJs"
```js
const request = require('node-fetch');
const headers = {
'Accept':'application/json'
};
fetch('https://amplitude.com/api/2/deletions/users?start_day=string&end_day=string',
{
method: 'GET',
headers: headers
})
.then(function(res) {
return res.json();
}).then(function(body) {
console.log(body);
});
```
=== "Ruby"
```ruby
require 'rest-client'
require 'json'
headers = {
'Accept' => 'application/json'
}
result = RestClient.get 'https://amplitude.com/api/2/deletions/users',
params: {
'start_day' => 'string',
'end_day' => 'string'
}, headers: headers
p JSON.parse(result)
```
=== "Python"
```python
import requests
headers = {
'Accept': 'application/json'
}
r = requests.get('https://amplitude.com/api/2/deletions/users', params={
'start_day': 'string', 'end_day': 'string'
}, headers = headers)
print r.json()
```
=== "Java"
```java
URL obj = new URL("https://amplitude.com/api/2/deletions/users?start_day=string&end_day=string");
HttpURLConnection con = (HttpURLConnection) obj.openConnection();
con.setRequestMethod("GET");
int responseCode = con.getResponseCode();
BufferedReader in = new BufferedReader(
new InputStreamReader(con.getInputStream()));
String inputLine;
StringBuffer response = new StringBuffer();
while ((inputLine = in.readLine()) != null) {
response.append(inputLine);
}
in.close();
System.out.println(response.toString());
```
=== "Go"
```go
package main
import (
"bytes"
"net/http"
)
func main() {
headers := map[string][]string{
"Accept": []string{"application/json"},
}
data := bytes.NewBuffer([]byte{jsonReq})
req, err := http.NewRequest("GET", "https://amplitude.com/api/2/deletions/users", data)
req.Header = headers
client := &http.Client{}
resp, err := client.Do(req)
// ...
}
```
<!--vale on-->
### Query parameters
|Name|Description|
|-----|------------|
|`start`| <span class="required">Required</span>. First hour included in data series, formatted `YYYYMMDDTHH`. For example, `20220201T05`.|
|`end` |<span class="required">Required</span>. Last hour included in data series, formatted `YYYYMMDDTHH` For example, `20220201T05`.|
### Response
The success response for a `GET` request contains these fields:
| <div class="big-column">Property</div> | Description |
| --- | --- |
| `day` | The day the deletion job is scheduled to begin. |
| `status` | The deletion job's status. <br> <br>**Staging**: The job hasn't started, and you can modify it. More deletion requests may get scheduled into this job and you can remove requests from this job. <br> <br>**Submitted**: The job is submitted to run. You can't modify it. <br> <br>**Done**: The job has finished running. You can't modify it. |
| `amplitude_ids` | List of the Amplitude Ids of users to delete. |
| `app` | Project or app ID. Appears if the deletion is applied to more than one project. |
The `amplitude_ids` key contains these fields:
| <div class="big-column">Name</div> | Description |
| --- | --- |
| `amplitude_id` | The Amplitude ID of the user to be deleted. |
| `requester` | The person who requested the Amplitude ID to be deleted. |
| `requested_on_day` | The day this deletion was requested. |
```json
[
{
"day": "string",
"amplitude_ids": [
{
"amplitude_id": 0,
"requested_on_day": "string",
"requester": "string"
}
],
"status": "string"
}
]
```
## Delete deletion job
Removes the specified ID from a deletion job.
`/api/2/deletions/users/AMPLITUDE_ID/YYYY-MM-DD`
### Example request
```bash
DELETE /api/2/deletions/users/12345/ HTTP/1.1
Host: amplitude.com
Authorization: Basic {{api-key}}:{{secret-key}} # credentials must be base64-encoded
{
"amplitude_ids": [
"amp_id_1",
"amp_id_2",
"..."
],
"user_ids": [
"user_id_1",
"user_id_2",
"..."
],
"requester": "employee@yourcompany.com"
}
```
### Path variables
|<div class="big-column">Name</div>| Description|
|----|-----|
|`amplitude_ids` or `user_ids`| Required. The `user_ids` or `amplitude_ids` to remove from the deletion job.|
|`date`| Required. Day the deletion is schedule for.|
### Response
A successful request returns a response with this schema:
| <div class="big-column">Property</div> | Description |
| --- | --- |
| `amplitude_id` | The Amplitude ID of the user to be deleted. |
| `requester` | The person who requested the Amplitude ID to be deleted. |
| `requested_on_day` | The day this deletion was requested. |
```json
{
"day": "string",
"amplitude_ids": [
{
"amplitude_id": 0,
"requested_on_day": "string",
"requester": "string"
}
],
"status": "string"
}
```
| <div class="big-column">Property</div> | Description |
| --- | --- |
| `day` | The day the deletion job is scheduled to begin. |
| `status` | The deletion job's status. <br> <br>**Staging**: The job hasn't started, and you can still modify it. More deletion requests may get scheduled into this job and you can remove requests from this job. <br> <br>**Submitted**: The job is submitted to run. You can't modify it. <br> <br>**Done**: The job has finished running. You can't modify it. |
| `amplitude_ids` | List of the Amplitude Ids of users to delete. |
The `amplitude_ids` key contains these fields:
| <div class="big-column">Name</div> | Description |
| --- | --- |
| `amplitude_id` | The Amplitude ID of the user to be deleted. |
| `requester` | The person who requested the Amplitude ID to be deleted. |
| `requested_on_day` | The day this deletion was requested. |
## Status codes
|Code|Message|
|----|---------|
|200|Success|
|400| Bad Request|
--8<-- "includes/abbreviations.md"