End-user deletions
Erase one end user's measurement data for a project, and poll the erasure to completion.
/v1/projects/:projectId/end-user-deletions- Auth
- Bearer
- Scope
- events:write
/v1/projects/:projectId/end-user-deletions/:deletionId- Auth
- Bearer
- Scope
- events:read
Erases one end user's measurement data for one project. Use it to fulfil a data-subject deletion request, and for the deletion obligations in measurement partner terms (Snap attribution terms, Google Ads App Attribution Partner requirements). Studio members can file the same request in MMA under Settings, Studio.
The erasure is irreversible. POST answers 202 with the request; the erasure runs in the background, and GET reports its status. A request stays running until every store that holds user-level rows is erased, including the measurement pipeline.
What is erased
Layers resolves the identifier to the installs, app user ids and SDK device ids linked to it in this project, then:
- Deletes the user's SDK events, sessions, app-user profile, device links, install attribution, reattributions, attributed clicks, revenue identity links and SDK health beacons.
- Irreversibly anonymizes rows that feed reported totals: install channel assignments, event and session rollup rows, cohort membership and revenue events. The identifier is replaced by a one-way token whose key is discarded when the erasure completes. Revenue events keep their amount, currency and dates; the customer id, email, transaction ids and raw store payload are removed.
- Keeps aggregates. Daily totals, cohort curves, ROAS and spend already computed keep their counts and are not restated. A day that is still inside a refresh window can drop by this user's contribution when that window next refreshes.
An identifier that links to more than 100 installs or app users is refused with status: "failed". That fan-out means a shared or placeholder id, and erasing it would erase other people.
If the app keeps running on the user's device, the SDK sends new events after the erasure. Stop collection on the device (the SDK's consent controls or reset()) before you file the request.
Create a deletion
projectIdstringrequiredProject ID.
identifierKindstringrequiredOne of `install_id`, `app_user_id`, `idfa`, `gaid`, `idfv`, `device_id` (the SDK device id). `idfa`, `gaid` and `idfv` match case-insensitively.identifierValuestringrequiredThe identifier exactly as your app sends it. 1 to 512 characters.
Send an Idempotency-Key header. A repeat request for the same identifier while one is open returns the open request.
curl -X POST "https://api.layers.com/v1/projects/prj_254a4ce1-f4ca-42b1-9e36-17ca45ef3d39/end-user-deletions" \
-H "Authorization: Bearer lp_..." \
-H "Idempotency-Key: 6f1c2b7e-dsr-42" \
-H "Content-Type: application/json" \
-d '{ "identifierKind": "app_user_id", "identifierValue": "user_a7f3c1d9" }'{
"deletion": {
"id": "eud_9b1f6c0e-2a47-4c1b-8f0e-5d2c7a31b8e4",
"projectId": "prj_254a4ce1-f4ca-42b1-9e36-17ca45ef3d39",
"identifierKind": "app_user_id",
"status": "pending",
"requestedAt": "2026-09-25T18:02:11Z",
"completedAt": null,
"error": null
}
}Get a deletion
curl "https://api.layers.com/v1/projects/prj_254a4ce1-f4ca-42b1-9e36-17ca45ef3d39/end-user-deletions/eud_9b1f6c0e-2a47-4c1b-8f0e-5d2c7a31b8e4" \
-H "Authorization: Bearer lp_..."status | Meaning | Your next action |
|---|---|---|
pending | Accepted, not started. | Poll again. |
running | Erasure in progress. | Poll again; a minute apart is enough. |
completed | Every store is erased. completedAt is set. | Record completion. Stop polling. |
failed | Stopped. error says why. | Fix the cause and submit again. When the Postgres store completed and only the ClickHouse store failed, the same request (same id) is reopened for ClickHouse; otherwise a new request starts. |
The response never echoes the identifier value. Layers keeps a SHA-256 hash of it on the request record.
Errors
| Status | Code | When |
|---|---|---|
404 | NOT_FOUND | The project or deletion is not in your organization. |
422 | VALIDATION | Unknown identifierKind, empty value, an extra field, or a malformed deletion id. |
503 | INTERNAL | The request is recorded but could not start. Submit it again to retry. |