Delete Contact PII
curl --request DELETE \
--url https://app.cometly.com/public-api/v1/contacts/{id}/pii \
--header 'Authorization: Bearer <token>'import requests
url = "https://app.cometly.com/public-api/v1/contacts/{id}/pii"
headers = {"Authorization": "Bearer <token>"}
response = requests.delete(url, headers=headers)
print(response.text)const options = {method: 'DELETE', headers: {Authorization: 'Bearer <token>'}};
fetch('https://app.cometly.com/public-api/v1/contacts/{id}/pii', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://app.cometly.com/public-api/v1/contacts/{id}/pii",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "DELETE",
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"net/http"
"io"
)
func main() {
url := "https://app.cometly.com/public-api/v1/contacts/{id}/pii"
req, _ := http.NewRequest("DELETE", url, nil)
req.Header.Add("Authorization", "Bearer <token>")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.delete("https://app.cometly.com/public-api/v1/contacts/{id}/pii")
.header("Authorization", "Bearer <token>")
.asString();require 'uri'
require 'net/http'
url = URI("https://app.cometly.com/public-api/v1/contacts/{id}/pii")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Delete.new(url)
request["Authorization"] = 'Bearer <token>'
response = http.request(request)
puts response.read_body{
"contact_id": 123,
"status": "<string>",
"message": "<string>",
"contact_ids": [
123
]
}Contacts
Delete Contact PII
Permanently scrub a contact’s personally identifiable information, by contact ID or by email
DELETE
/
public-api
/
v1
/
contacts
/
{id}
/
pii
Delete Contact PII
curl --request DELETE \
--url https://app.cometly.com/public-api/v1/contacts/{id}/pii \
--header 'Authorization: Bearer <token>'import requests
url = "https://app.cometly.com/public-api/v1/contacts/{id}/pii"
headers = {"Authorization": "Bearer <token>"}
response = requests.delete(url, headers=headers)
print(response.text)const options = {method: 'DELETE', headers: {Authorization: 'Bearer <token>'}};
fetch('https://app.cometly.com/public-api/v1/contacts/{id}/pii', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://app.cometly.com/public-api/v1/contacts/{id}/pii",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "DELETE",
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"net/http"
"io"
)
func main() {
url := "https://app.cometly.com/public-api/v1/contacts/{id}/pii"
req, _ := http.NewRequest("DELETE", url, nil)
req.Header.Add("Authorization", "Bearer <token>")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.delete("https://app.cometly.com/public-api/v1/contacts/{id}/pii")
.header("Authorization", "Bearer <token>")
.asString();require 'uri'
require 'net/http'
url = URI("https://app.cometly.com/public-api/v1/contacts/{id}/pii")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Delete.new(url)
request["Authorization"] = 'Bearer <token>'
response = http.request(request)
puts response.read_body{
"contact_id": 123,
"status": "<string>",
"message": "<string>",
"contact_ids": [
123
]
}Overview
This endpoint permanently scrubs the personally identifiable information (PII) stored for a contact, while preserving the contact record itself and its event history for reporting continuity. You can target the contact either of two ways:- By path ID:
DELETE /contacts/{id}/pii - By email:
DELETE /contacts/pii?email={email}
id or email must be provided — supplying both, or neither, returns a 422.
This is an asynchronous operation. The request validates and resolves the target contact synchronously, then queues the actual scrub and returns immediately with 202 Accepted. The PII removal itself typically completes within a few minutes; call Get Contact afterward to confirm the fields have been cleared.
What gets deleted
- All emails, phones, names, locations, devices, IPs, tracking IDs, and comet tokens ever associated with the contact — including data inherited from profiles that were merged into this contact.
- All raw browsing-session hits for the contact (IPs, fingerprints, tokens, and URL query strings). After this completes,
include_browsing_session_dataon Get Contact returns empty, and the contact’s page-view hits no longer contribute to analytics counts. - On the contact record itself:
name,email,phone,location,city,state,country,device_type,os,browser,language, and all 30 custom fields (profile_field_1–profile_field_30) are set tonull.
What is kept
- The contact record itself — its
idandcreated_atare preserved. - Its events and journey — touchpoints and conversions, including their metadata, remain intact.
- Its merge history.
Event metadata can still contain PII that arrived inside a webhook payload or a touchpoint’s URL query string — deleting a contact’s PII does not scrub individual events. To remove PII from a specific event, delete it directly via Delete Event.
Path Parameters
integer
The unique identifier of the contact whose PII should be deleted. Required when not using
email. Merged profile aliases are automatically resolved to the current canonical contact. Minimum: 1.Query Parameters
string
The email address of the contact whose PII should be deleted. Required when not using
id. Matches against both the contact’s full email history (including emails inherited from merged profiles) and its current primary email. If more than one distinct contact matches the email, the request is rejected with a 409 — delete by id instead to disambiguate.Response
Success Response
Returns202 Accepted. The scrub is queued, not yet complete.
integer
The canonical contact id (after resolving merged-profile aliases) whose PII was queued for deletion.
string
Always
queued.string
Human-readable confirmation that the deletion has been queued.
Error Response
string
Error description explaining what went wrong.
integer[]
Only present on a
409 response. The sorted list of canonical contact ids that matched the given email. Nothing is deleted when this error is returned — retry the request once per id to disambiguate.Example Requests
Delete by Contact ID
curl -X DELETE "https://app.cometly.com/public-api/v1/contacts/12345/pii" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Accept: application/json"
const contactId = 12345;
const response = await fetch(`https://app.cometly.com/public-api/v1/contacts/${contactId}/pii`, {
method: 'DELETE',
headers: {
'Authorization': 'Bearer YOUR_API_KEY',
'Accept': 'application/json'
}
});
const data = await response.json();
console.log('Deletion queued:', data);
<?php
$contactId = 12345;
$url = 'https://app.cometly.com/public-api/v1/contacts/' . $contactId . '/pii';
$headers = [
'Authorization: Bearer YOUR_API_KEY',
'Accept: application/json'
];
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'DELETE');
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$response = curl_exec($ch);
$result = json_decode($response, true);
curl_close($ch);
?>
import requests
contact_id = 12345
url = f'https://app.cometly.com/public-api/v1/contacts/{contact_id}/pii'
headers = {
'Authorization': 'Bearer YOUR_API_KEY',
'Accept': 'application/json'
}
response = requests.delete(url, headers=headers)
result = response.json()
Delete by Email
curl -X DELETE "https://app.cometly.com/public-api/v1/contacts/pii?email=john%40example.com" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Accept: application/json"
const email = 'john@example.com';
const response = await fetch(`https://app.cometly.com/public-api/v1/contacts/pii?email=${encodeURIComponent(email)}`, {
method: 'DELETE',
headers: {
'Authorization': 'Bearer YOUR_API_KEY',
'Accept': 'application/json'
}
});
const data = await response.json();
console.log('Deletion queued:', data);
<?php
$email = 'john@example.com';
$url = 'https://app.cometly.com/public-api/v1/contacts/pii?' . http_build_query(['email' => $email]);
$headers = [
'Authorization: Bearer YOUR_API_KEY',
'Accept: application/json'
];
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'DELETE');
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$response = curl_exec($ch);
$result = json_decode($response, true);
curl_close($ch);
?>
import requests
email = 'john@example.com'
url = 'https://app.cometly.com/public-api/v1/contacts/pii'
headers = {
'Authorization': 'Bearer YOUR_API_KEY',
'Accept': 'application/json'
}
response = requests.delete(url, headers=headers, params={'email': email})
result = response.json()
Status Codes
| Status Code | Description |
|---|---|
| 202 | PII deletion successfully queued |
| 401 | Missing or invalid API key |
| 403 | API key doesn’t have permission or subscription is inactive |
| 404 | Contact not found (unknown id or no contact matches email) |
| 409 | Multiple contacts match the given email — see contact_ids in the response and retry by id |
| 422 | Invalid parameters (both id and email provided, neither provided, or invalid format) |
| 429 | Too many requests - rate limit exceeded. See Rate Limiting |
Notes
- Rate Limit: This endpoint has a limit of 60 requests per minute per Space. See Rate Limiting for details.
- Not idempotent on missing targets: unlike Delete Event, which returns
200for an ID that no longer exists, this endpoint returns404when theidoremaildoesn’t resolve to a contact in your space. - Asynchronous: a
202response means the deletion has been queued, not completed. Poll Get Contact to confirm the PII fields have been cleared. - Ambiguous email matches nothing: if
emailmatches more than one distinct contact, the request is rejected with409and no data is deleted. Look up the individual contact ids (viacontact_idsin the response, or List Contacts) and call this endpoint once perid. - Merged profiles: if you pass an
idthat was merged into another profile, the deletion applies to the canonical (current) profile.
This operation is permanent and cannot be undone. The contact record, its events, and its merge history are preserved — only identifying data is scrubbed.