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 values key. A bare array such as ["[email protected]"] does not parse and returns 400.

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"
    }
  }
}
  • successes maps each accepted identifier to a unique request_id. Store these for status tracking.
  • errors maps 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 successes with the original request_id and 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:

StateMeaning
initialThe request has been accepted.
primaryThe first round of data removal is underway.
archivePrimary data is cleared; archived copies are being cleared.
secondaryArchived copies are cleared; the final removal pass is underway.
doneAll data has been removed. This is the final state.
🚧

Do not consider a deletion complete until completed is true (state done). Reaching secondary is 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

ScenarioRecommended action
Deleting a list of users for GDPRUse the bulk endpoint with identifiers in the JSON body
Getting 404s from the single-value endpointSwitch to the bulk endpoint, which accepts identifiers in any state
Sending thousands of identifiersOne body holds many thousands; split very large jobs into a few requests
Checking if a deletion is completePoll GET /v2/identity/deletestatus/{request_id} until completed is true
Resubmitting an identifier you already sentSafe; returns the original request_id, no duplicate
Mixing emails and member IDsSubmit 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.