GDPR Requests
The ten endpoints that read a client's data removal requests, settle them, and manage the feature's settings.
Overview
A client can ask for their data to be removed. These endpoints read those requests, settle them, and manage the feature's own settings.
Removal has three scopes and no way back: cutting access, anonymising the identifying data, deleting the user outright. Which one ran is in remove_action on the response.
Reference
Listing Requests
Returns the list of data requests coming from clients.
remove.pending.0 on the last one.curl -G 'https://panel.example.com/api/v1/admin/clients/gdpr-requests' \
-H "Authorization: Bearer $API_KEY" \
-d limit=50const url = new URL('https://panel.example.com/api/v1/admin/clients/gdpr-requests');
url.searchParams.set('limit', '50');
const res = await fetch(url, { headers: { Authorization: `Bearer ${apiKey}` } });
const body = await res.json();$url = 'https://panel.example.com/api/v1/admin/clients/gdpr-requests?' . http_build_query(['limit' => 50]);
$ch = curl_init($url);
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . $apiKey],
]);
$body = json_decode(curl_exec($ch), true);
curl_close($ch);$response = Api::Clients()->GetGdprRequests([], ['limit' => 50]);Request Detail
Returns one request. This is what you read before deciding.
block_access, identifying_data or all.curl 'https://panel.example.com/api/v1/admin/clients/gdpr-requests/9' \
-H "Authorization: Bearer $API_KEY"const res = await fetch('https://panel.example.com/api/v1/admin/clients/gdpr-requests/9', {
headers: { Authorization: `Bearer ${apiKey}` },
});
const body = await res.json();$ch = curl_init('https://panel.example.com/api/v1/admin/clients/gdpr-requests/9');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . $apiKey],
]);
$body = json_decode(curl_exec($ch), true);
curl_close($ch);$response = Api::Clients()->GetGdprRequest(['rid' => 9]);
$request = $response['data']['request'];
// Asking for 'all' on a client with invoices can collide with legal retention.
$hasInvoices = $request['invoice_count'] > 0;Settling a Request
Approves or refuses the request. On approval the data removal runs in the scope you choose.
remove, anonymize, destroy, or cancelled to refuse.block_access cuts access, identifying_data anonymises the identifying data, all deletes the user.none, block_access, identifying_data or all.0 or 1.status is not one of the allowed values.curl -X POST 'https://panel.example.com/api/v1/admin/clients/gdpr-requests/9/process' \
-H "Authorization: Bearer $API_KEY" \
-H 'Content-Type: application/json' \
-d '{"status":"anonymize","remove_type":"identifying_data","status_note":"Request verified","notification":true}'const res = await fetch('https://panel.example.com/api/v1/admin/clients/gdpr-requests/9/process', {
method: 'POST',
headers: {
Authorization: `Bearer ${apiKey}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
status: 'anonymize',
remove_type: 'identifying_data',
status_note: 'Request verified',
notification: true,
}),
});
const body = await res.json();$ch = curl_init('https://panel.example.com/api/v1/admin/clients/gdpr-requests/9/process');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . $apiKey,
'Content-Type: application/json',
],
CURLOPT_POSTFIELDS => json_encode([
'status' => 'anonymize',
'remove_type' => 'identifying_data',
'status_note' => 'Request verified',
]),
]);
$body = json_decode(curl_exec($ch), true);
curl_close($ch);$response = Api::Clients()->ProcessGdprRequest([
'rid' => 9,
'status' => 'anonymize',
'remove_type' => 'identifying_data',
'status_note' => 'Request verified',
]);
$applied = $response['data']['remove_action'] ?? 'none';{
"data": {
"processed": true,
"status": "anonymize",
"remove_action": "identifying_data",
"blacklist": 0
}
}{
"error": {
"code": "blocked_by_gate",
"message": "The removal was blocked by a hook."
}
}Deleting a Request
Deletes the request record. It does not touch the client's data; only the record goes.
curl -X DELETE 'https://panel.example.com/api/v1/admin/clients/gdpr-requests/9' \
-H "Authorization: Bearer $API_KEY"const res = await fetch('https://panel.example.com/api/v1/admin/clients/gdpr-requests/9', {
method: 'DELETE',
headers: { Authorization: `Bearer ${apiKey}` },
});
const body = await res.json();$ch = curl_init('https://panel.example.com/api/v1/admin/clients/gdpr-requests/9');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CUSTOMREQUEST => 'DELETE',
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . $apiKey],
]);
$body = json_decode(curl_exec($ch), true);
curl_close($ch);$response = Api::Clients()->DeleteGdprRequest(['rid' => 9]);Reading the Settings
Returns whether the feature is on, which contract page is attached, and the canned denial reasons.
curl 'https://panel.example.com/api/v1/admin/clients/gdpr-settings' \
-H "Authorization: Bearer $API_KEY"const res = await fetch('https://panel.example.com/api/v1/admin/clients/gdpr-settings', {
headers: { Authorization: `Bearer ${apiKey}` },
});
const body = await res.json();$ch = curl_init('https://panel.example.com/api/v1/admin/clients/gdpr-settings');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . $apiKey],
]);
$body = json_decode(curl_exec($ch), true);
curl_close($ch);$response = Api::Clients()->GetGdprSettings();Saving the Settings
Turns the feature on or off and attaches the contract page.
gdpr-contracts.curl -X PUT 'https://panel.example.com/api/v1/admin/clients/gdpr-settings' \
-H "Authorization: Bearer $API_KEY" \
-H 'Content-Type: application/json' \
-d '{"enabled":true,"required":true,"contract_page_id":12}'const res = await fetch('https://panel.example.com/api/v1/admin/clients/gdpr-settings', {
method: 'PUT',
headers: {
Authorization: `Bearer ${apiKey}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({ enabled: true, required: true, contract_page_id: 12 }),
});
const body = await res.json();$ch = curl_init('https://panel.example.com/api/v1/admin/clients/gdpr-settings');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CUSTOMREQUEST => 'PUT',
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . $apiKey,
'Content-Type: application/json',
],
CURLOPT_POSTFIELDS => json_encode([
'enabled' => true,
'required' => true,
'contract_page_id' => 12,
]),
]);
$body = json_decode(curl_exec($ch), true);
curl_close($ch);$response = Api::Clients()->SaveGdprSettings([
'enabled' => true,
'required' => true,
'contract_page_id' => 12,
]);Searching Contract Pages
Returns the pages that can be attached as the contract. This is where contract_page_id comes from.
contract_page_id.curl 'https://panel.example.com/api/v1/admin/clients/gdpr-contracts' \
-H "Authorization: Bearer $API_KEY"const res = await fetch('https://panel.example.com/api/v1/admin/clients/gdpr-contracts', {
headers: { Authorization: `Bearer ${apiKey}` },
});
const body = await res.json();$ch = curl_init('https://panel.example.com/api/v1/admin/clients/gdpr-contracts');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . $apiKey],
]);
$body = json_decode(curl_exec($ch), true);
curl_close($ch);$response = Api::Clients()->GetGdprContracts();Listing Denial Reasons
Returns the canned texts used when refusing a request.
curl 'https://panel.example.com/api/v1/admin/clients/gdpr-denial-reasons' \
-H "Authorization: Bearer $API_KEY"const res = await fetch('https://panel.example.com/api/v1/admin/clients/gdpr-denial-reasons', {
headers: { Authorization: `Bearer ${apiKey}` },
});
const body = await res.json();$ch = curl_init('https://panel.example.com/api/v1/admin/clients/gdpr-denial-reasons');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . $apiKey],
]);
$body = json_decode(curl_exec($ch), true);
curl_close($ch);$response = Api::Clients()->GetGdprDenialReasons();Adding a Denial Reason
Adds a new canned reason to the list.
value was empty.curl -X POST 'https://panel.example.com/api/v1/admin/clients/gdpr-denial-reasons' \
-H "Authorization: Bearer $API_KEY" \
-H 'Content-Type: application/json' \
-d '{"value":"The legal retention period has not ended"}'const res = await fetch('https://panel.example.com/api/v1/admin/clients/gdpr-denial-reasons', {
method: 'POST',
headers: {
Authorization: `Bearer ${apiKey}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({ value: 'The legal retention period has not ended' }),
});
const body = await res.json();$ch = curl_init('https://panel.example.com/api/v1/admin/clients/gdpr-denial-reasons');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . $apiKey,
'Content-Type: application/json',
],
CURLOPT_POSTFIELDS => json_encode(['value' => 'The legal retention period has not ended']),
]);
$body = json_decode(curl_exec($ch), true);
curl_close($ch);$response = Api::Clients()->AddGdprDenialReason(['value' => 'The legal retention period has not ended']);Deleting a Denial Reason
Removes a reason from the list. You send the text itself, not an id.
value was empty.curl -X DELETE 'https://panel.example.com/api/v1/admin/clients/gdpr-denial-reasons' \
-H "Authorization: Bearer $API_KEY" \
-H 'Content-Type: application/json' \
-d '{"value":"The legal retention period has not ended"}'const res = await fetch('https://panel.example.com/api/v1/admin/clients/gdpr-denial-reasons', {
method: 'DELETE',
headers: {
Authorization: `Bearer ${apiKey}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({ value: 'The legal retention period has not ended' }),
});
const body = await res.json();$ch = curl_init('https://panel.example.com/api/v1/admin/clients/gdpr-denial-reasons');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CUSTOMREQUEST => 'DELETE',
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . $apiKey,
'Content-Type: application/json',
],
CURLOPT_POSTFIELDS => json_encode(['value' => 'The legal retention period has not ended']),
]);
$body = json_decode(curl_exec($ch), true);
curl_close($ch);$response = Api::Clients()->DeleteGdprDenialReason(['value' => 'The legal retention period has not ended']);Pitfalls
The request detail carries invoice_count. In most countries invoices fall under a legal retention period, so asking for all on a client with invoices can collide with that duty. That is why the field is in the response.
A blocked_by_gate error does not mean your request was wrong; it means an addon in the installation blocked the removal. If you wrote your own hook, look there first.
The delete endpoint only removes the request record. The client's data stays exactly as it was; removal runs only through the process endpoint.
Related Articles
Thanks for your feedback!
Our support team is here around the clock for anything you can't find above.