Bulk User Deletion
Delete large lists of user profiles for GDPR and CCPA compliance using the bulk identity deletion endpoint.
Overview
Lytics provides a bulk identity deletion endpoint for removing large lists of user profiles in a single workflow. It is the right tool whenever you need to fulfill privacy requests at scale. That includes processing a batch of GDPR right-to-erasure or CCPA deletion requests, honoring a suppression list handed off from a consent management platform, or clearing profiles as part of a data retention policy.
This guide is written for the data engineers and privacy teams who build and operate those deletion workflows. It explains why the bulk endpoint is the correct choice for batch work, why the single-value endpoint can return 404 errors partway through a large job, and how to structure your requests so a run completes cleanly and stays auditable from start to finish.
Deleting a single profile on demand? You can do that from the UI (see Compliance) or with the single-value API endpoint described in Identity Deletion. This guide focuses on batch deletion.
The two delete endpoints
Lytics exposes two endpoints for deleting user profiles. They behave differently, and choosing the right one is the single most important decision in a deletion workflow.
Single-value delete
DELETE /v2/identity/{table}/{key}/{value}
This endpoint resolves the full profile at the time of the request and then queues it for deletion. If the profile cannot be fully resolved (for example it is partially deleted or otherwise in an inconsistent state), it returns 404. It is best suited to one-off, on-demand deletions.
Bulk delete
DELETE /v2/identity/queue/bulk/{table}/{field}
This endpoint queues deletion requests without resolving each profile at request time. Profiles in any state, including partially deleted or inconsistent ones, are accepted into the queue and processed asynchronously. You will not get 404 errors for profiles the single-value endpoint cannot resolve.
This is the correct endpoint for any batch deletion workflow.
Why single-value deletes return 404
Because the single-value endpoint resolves a profile before queuing it, it returns 404 when that resolution fails. In a large job, even a small percentage of unresolvable profiles will cause failures partway through. Resolution typically fails for one of three reasons.
Complex or inconsistent profiles. Some profiles cannot be fully resolved at request time, such as those with an unusually large or tangled identity graph. These can fail to resolve and return 404.
Partially deleted profiles. If a deletion was already submitted and is still processing, a new delete request for the same identifier may not find a resolvable profile and returns 404.
Events re-ingested after deletion. If new events for a previously deleted user arrive (for example from a delayed data pipeline or a historical backfill), partial profile data can be recreated and return 404 on single-value delete. Events from before the deletion remain suppressed from exports, and activity that arrives after the deletion is treated as a new profile.
In every case the fix is the same: use the bulk endpoint, which does not resolve the profile at request time.
Building a bulk deletion workflow
Send identifiers in the JSON body
The bulk endpoint accepts application/json. Put the table and field in the URL path, and send the identifiers as a JSON object with a values array in the request body.
Sending identifiers in the body rather than the URL avoids URL length limits and keeps user identifiers out of URL-based request logs, which is better practice for handling personally identifiable information (PII).
curl -X DELETE \
'https://api.lytics.io/v2/identity/queue/bulk/user/email' \
-H 'Authorization: $LIOKEY' \
-H 'Content-Type: application/json' \
-d '{"values": ["[email protected]", "[email protected]", "[email protected]"]}'A single request body can hold many thousands of identifiers. For very large jobs, split the work across multiple requests so responses stay manageable and one failed request does not hold up the whole run.
The body must be a JSON object with a
valueskey. A bare array such as["[email protected]"]does not parse and returns400.
Parse the response and store request IDs
The bulk endpoint returns 202 with a JSON object containing two maps:
{
"data": {
"successes": {
"[email protected]": "req-abc123",
"[email protected]": "req-def456"
},
"errors": {
"bad-value": "reason the value was not queued"
}
}
}successesmaps each accepted identifier to a uniquerequest_id. Store these for status tracking.errorsmaps each identifier that was not queued to the reason it failed, usually a malformed value or one that does not match the specified field. Log and review these.
Resubmitting an identifier you already sent is safe. It comes back in
successeswith the originalrequest_idand does not create a duplicate deletion.
Remove invalid values before submitting
Exported user lists often contain values like null, 0, NaN, and empty strings from upstream data quality issues. These do not match real profiles and will appear in the errors map. Filter them out before building your request.
Send one field type per request
The bulk endpoint is scoped to a single table and field. If you have multiple identifier types to delete (for example email addresses and member IDs), submit separate requests:
# Request 1: delete by email
DELETE /v2/identity/queue/bulk/user/email
# Request 2: delete by member ID
DELETE /v2/identity/queue/bulk/user/member_id
Track deletion progress
Deletion is asynchronous. A queued request does not mean the data is gone yet. Use the status endpoints to confirm progress:
# Status of a specific request
GET /v2/identity/deletestatus/{request_id}
# All deletion requests for your account
GET /v2/identity/deletestatus/list
# Overview of all requests by state
GET /v2/identity/deletestatus/overview
The status response includes a state and a completed flag. A request moves through these states as its data is removed from each storage layer:
| State | Meaning |
|---|---|
initial | The request has been accepted. |
primary | The first round of data removal is underway. |
archive | Primary data is cleared; archived copies are being cleared. |
secondary | Archived copies are cleared; the final removal pass is underway. |
done | All data has been removed. This is the final state. |
Do not consider a deletion complete until
completedistrue(statedone). Reachingsecondaryis not the end. A final removal pass still follows it. Full removal can take hours to days depending on the volume of data associated with the profile.
A deleted user's historical events are excluded from exports immediately, even while physical removal is still in progress.
Required API permissions
The API token must have Entity: Delete permission. A read-only token returns a permission-denied error. Verify your token's permissions in your Lytics account settings before running bulk deletions.
Deleting already-deleted or partial profiles
If an identifier returns 404 on single-value delete but you still need it removed, submit it to the bulk queue. The deletion system processes whatever data remains. Submitting a delete for a profile that is already partially or fully cleaned up is safe. It will be a no-op, or it will appear in successes with a request_id.
Quick reference
| Scenario | Recommended action |
|---|---|
| Deleting a list of users for GDPR | Use the bulk endpoint with identifiers in the JSON body |
| Getting 404s from the single-value endpoint | Switch to the bulk endpoint, which accepts identifiers in any state |
| Sending thousands of identifiers | One body holds many thousands; split very large jobs into a few requests |
| Checking if a deletion is complete | Poll GET /v2/identity/deletestatus/{request_id} until completed is true |
| Resubmitting an identifier you already sent | Safe; returns the original request_id, no duplicate |
| Mixing emails and member IDs | Submit as separate bulk requests, one field type per request |
For compliance workflows driven by OneTrust or a similar consent management platform, see Managing Consumer Data Subject Requests with OneTrust.