Bulk End-User Deletion API

Overview

The Bulk End-User Deletion API erases all personal data associated with one or more end users across Ada’s systems. A single request can carry up to 1000 identifiers and is processed as an asynchronous job. Submit a request to receive a job_id, then poll the job to track progress and review the outcome for each identifier.

This API supports compliance with data deletion requirements under applicable laws, such as GDPR.

This API replaces the single-record Data Compliance API, which is now deprecated. Use the Bulk End-User Deletion API for all end-user data deletion.

Identifiers

Each request supplies a list of identifiers. Every identifier has a type, a value, and — for variable_match only — a variable naming the stored variable to match against.

TypeDescription
emailMatches end users by email address. Values are normalized and matched case-insensitively.
chatter_idMatches by a legacy Ada end-user record ID.
external_idMatches by the ID you assign to an end user. Values are trimmed and lowercased at intake.
end_user_idMatches by Ada’s end-user identifier.
variable_matchMatches every end user whose meta variable named variable currently equals value — for example an end user’s email, language, or any custom metadata key.

What variable_match matches: variable names a meta variable on the end user. When you set a profile through the End Users API, both the profile fields (first_name, last_name, display_name, avatar, email, language) and every key in the metadata object are stored as meta variables — variable_match can target any of them. It matches on the end user’s latest value for that variable (exact match). Global or session variables set during a conversation are not matched.

Limits. A single request accepts at most 1000 identifiers, and the request body may be at most 512 KB — a larger body returns 413. Each identifier may match at most 1000 end users — this ceiling is shared by variable_match and by the fan-out of an email match. An identifier that matches more than 1000 end users is reported as exceeds_limit and erases nothing; a variable_match that matches zero end users is reported as not_found.

Dispositions

The job records a disposition for every submitted identifier. Inspect the per-identifier dispositions to understand exactly what was erased.

DispositionMeaning
acceptedThe identifier matched end users who were queued for erasure.
not_foundNo end users matched the identifier.
invalidThe identifier was malformed or unusable. reason is populated.
exceeds_limitThe identifier matched more than 1000 end users, so nothing was erased. reason is populated.
duplicateThe identifier repeated another identifier in the same request.

Each disposition also reports matched_chatters, the number of end users matched by that identifier. A non-accepted disposition may carry a reason explaining the outcome: it is always set for invalid and exceeds_limit, and for not_found on ID-based identifiers, but can be null for a not_found email or variable_match selector — so don’t rely on reason being present based on disposition alone.

Job lifecycle

A job moves through the following states:

StatusMeaning
queuedThe job was accepted and is waiting to be processed. Non-terminal.
in_progressErasure is underway. Non-terminal.
completedEvery matched end user was erased.
partialNot every identifier was accepted or fully erased. This includes the case where zero identifiers were accepted.
failedThe job could not be processed. failure_reason is set.

Because partial covers a range of outcomes — including none accepted — do not rely on the top-level status alone. Inspect the per-identifier disposition array to see what happened to each identifier.

On a failed job, failure_reason carries an opaque, machine-readable diagnostic. Its values are subject to change, so treat it as a signal rather than a stable contract; to understand outcomes per identifier, use each identifier’s disposition and reason.

accepted and completed mean different things. An accepted disposition means an identifier’s end users were queued and submitted to Ada’s erasure engine — not that they are erased yet. The job stays in_progress while the engine erases across every data store, and only reaches completed once Ada confirms that erasure finished. Because the engine runs downstream on a daily cycle, reaching completed can take up to 30 days. This delay is expected behavior.

Resubmitting a request

Two independent windows govern what happens when the same identifier is submitted more than once. They have different lengths and different effects.

24-hour job idempotency

Set client_reference to make a request idempotent. An identical request — the same client_reference and the same set of identifiers — resubmitted within 24 hours returns the original job instead of creating a new one.

Requests that omit client_reference are not deduplicated. Each one creates a new job, even when it is identical to a request sent moments earlier.

7-day identifier cooldown

The erasure engine rejects an identifier that was already submitted within the previous 7 days. This window is separate from the 24-hour idempotency above, and it applies whether or not client_reference is set.

Because the two windows differ in length, a resubmission can fall between them. A request repeated after three days is outside the 24-hour idempotency window, so it creates a new job — but its identifiers are still inside the 7-day cooldown, so the engine rejects them and the job ends failed.

A single identifier in cooldown can fail an entire job. The engine evaluates a request as a unit and does not report which identifier conflicted, so unless the earlier request provably covered everything in the new one, the whole job fails rather than risk silently skipping identifiers the earlier request did not cover.

To integrate safely against both windows:

  • Set client_reference on every request.
  • Poll job status rather than treating the 202 as confirmation that erasure finished.
  • Track which identifiers have been submitted, and wait 7 days before resubmitting one.

Rate limit

The Bulk End-User Deletion API is subject to the global rate limits.