# Data Erasure API

The Data Erasure API is the self-serve way to erase a subject's personal data from Pushwoosh (GDPR right to erasure). Erasure is irreversible: use it to mirror the Control Panel's [Data requests](/product/account-management-and-security/data-requests/) page and the [Erase personal data](/product/audience-data-and-segmentation/user-explorer/#erase-personal-data) action on a subscriber's card from your own systems.

## Base URL

```
https://rpc-api.svc-nue.pushwoosh.com
```

All endpoints are served over HTTPS. Requests and responses use `application/json`, except [Upload](#upload), which takes `multipart/form-data`.

## Authentication

Every request must include an `Authorization` header with your [Server API token](/developer/api-reference/api-access-token/#server-api-token):

```
Authorization: Api YOUR_API_TOKEN
```

The token must have modify access to every application named in `application_codes`. That's the same access a token needs to write device data on those applications.

## Conventions

* **Field naming:** request bodies accept `lowerCamelCase` (for example, `applicationCodes`, `identifierType`). Responses are marshaled using the proto field names, in `snake_case` (`task_id`, `identifier_hash`, and so on). The examples and object references below use that casing.
* **Identifiers:** a subject is addressed by `identifier_type` (`SUBJECT_IDENTIFIER_USER_ID`, `SUBJECT_IDENTIFIER_HWID`, or `SUBJECT_IDENTIFIER_EMAIL`) plus a list of `identifiers` of that one type. An email identifier is matched case-insensitively.
* **Dry run first, then quote it:** [Create](#create) with `dry_run: true` only counts what would be erased and writes nothing. Quoting that dry run's `task_id` as `confirmation_dry_run_task_id` on the real call is optional, but if you quote one, it must match that dry run's own applications and identifiers exactly, and be called within 15 minutes of it finishing. Otherwise the call is refused.
* **Reports never carry the identifier in the clear.** Both [GetTaskReport](#gettaskreport) and [GetJournalReport](#getjournalreport) report each identifier as its sha256 hash (`identifier_hash`), and the report body is CSV encoded as base64 inside the JSON response.

### Error responses

| HTTP status | Meaning |
| :---- | :---- |
| `400 Bad Request` | Invalid argument, such as an empty `identifier_type`, `identifiers`, or `application_codes`, more than 10,000 identifiers, more than 50 applications, identifiers × applications over 50,000, an empty identifier string, or (Upload only) a missing/unreadable CSV file. Also returned as `FailedPrecondition` on the wire when a quoted `confirmation_dry_run_task_id` doesn't match the finished dry run it names, is too old (over 15 minutes), or isn't a finished dry run at all. |
| `401 Unauthorized` | Missing or invalid `Authorization` header. |
| `403 Forbidden` | The token has no modify/device-data right on one of the requested applications, or the caller may not read the requested task/report. |
| `404 Not Found` | The task was not found, or doesn't belong to the caller's account. |
| `413 Payload Too Large` | (Upload only) the uploaded file is over 5 MB. |
| `429 Too Many Requests` | The account already started 20 data erasure tasks (dry runs included) in the last 24 hours. |

## Endpoints

| Method | Path | Description |
| :---- | :---- | :---- |
| `POST` | `/api/data_erasure/tasks` | Create an erasure (or dry-run) task |
| `POST` | `/api/data_erasure/upload` | Create an erasure task from an uploaded CSV of identifiers |
| `GET` | `/api/data_erasure/tasks` | List the account's erasure tasks |
| `GET` | `/api/data_erasure/tasks/{task_id}` | Get one erasure task |
| `GET` | `/api/data_erasure/tasks/{task_id}/rows` | List one task's per-identifier results |
| `GET` | `/api/data_erasure/tasks/{task_id}/report` | Download one task's report as CSV |
| `GET` | `/api/data_erasure/report` | Download every task of a period as one CSV |

## Create

Starts erasing the personal data of the listed subjects in the listed applications. With `dry_run: true` the task only counts what would be erased.

`POST` `/api/data_erasure/tasks`

### Request body

| Parameter | Type | Required | Description |
| :---- | :---- | :---- | :---- |
| `identifierType` | string | Yes | `SUBJECT_IDENTIFIER_USER_ID`, `SUBJECT_IDENTIFIER_HWID`, or `SUBJECT_IDENTIFIER_EMAIL`. |
| `identifiers` | array of strings | Yes | Subject identifiers of that one type, up to 10,000 per task, no empty values. |
| `applicationCodes` | array of strings | Yes | [Application codes](/developer/api-reference/api-identifiers/#application-code) to erase from, up to 50. There's no way to erase from every application of the account in one call, so name each one explicitly. `identifiers.length × applicationCodes.length` must not exceed 50,000, because the report holds one row per identifier per application. |
| `dryRun` | boolean | No | Counts what would be erased and writes nothing. Defaults to `false`. |
| `confirmationDryRunTaskId` | integer | No | `task_id` of a finished dry run that measured this exact erase. See [Conventions](#conventions). |

##### Request example

```json
{
  "identifierType": "SUBJECT_IDENTIFIER_EMAIL",
  "identifiers": ["subject@example.com"],
  "applicationCodes": ["XXXXX-XXXXX"],
  "dryRun": true
}
```

### Response

| Field | Type | Description |
| :---- | :---- | :---- |
| `task_id` | integer | Id of the created task. Use it with every other endpoint on this page. |

<Aside type="danger">
Erasure cannot be undone. Erasing a subject removes their data in every named application, but does not cancel messages already scheduled or queued for that subject, for example a delayed send or a running journey's next step.
</Aside>

## Upload

Creates the same kind of task as [Create](#create), but the identifiers come from an uploaded CSV instead of a JSON array. Use this for a bulk subject list.

`POST` `/api/data_erasure/upload`

### Request (multipart/form-data)

| Field | Required | Description |
| :---- | :---- | :---- |
| `file` | Yes | CSV file, up to 5 MB, one identifier per row, in the single column. The first row may name the column (`user_id`, `userid`, `user id`, `hwid`, `email`, `identifier`, or `subject`, case-insensitively) instead of holding an identifier. That row is then skipped as a header, not counted. Blank rows are skipped and counted separately. Duplicate identifiers (case-insensitively, for email) are collapsed to one and counted separately. |
| `identifier_type` | Yes | `user_id`, `hwid`, or `email`, lowercase, a different casing from the JSON `identifierType` enum values used elsewhere on this page. One type applies to the whole file. |
| `application_codes` | Yes | Comma-separated application codes. |
| `dry_run` | No | `true`/`false`. |
| `confirmation_dry_run_task_id` | No | Same as `Create`. |

### Response

```json
{
  "task_id": 123,
  "identifier_count": 480,
  "duplicate_count": 3,
  "skipped_rows": 1
}
```

## ListTasks

Lists the erasures of the account, newest first: the audit journal of who erased what and when. Dry runs are left out unless `includeDryRuns` is set.

`GET` `/api/data_erasure/tasks`

### Query parameters

| Parameter | Type | Description |
| :---- | :---- | :---- |
| `limit` / `offset` | integer | Paging. |
| `includeDryRuns` | boolean | Include dry-run (measurement-only) tasks. |
| `from` / `to` | string (RFC 3339) | Keep tasks started in `[from, to)`. |
| `applicationCode` | string | Keep tasks that cover this application. |
| `initiatorUserId` | integer | Keep tasks started by this Control Panel user. `0` (default) keeps every initiator. |
| `initiatorTokenId` | integer | Keep tasks started by this API token. `0` (default) keeps every initiator. |
| `initiatorEmail` | string | Keep tasks started by this Control Panel user, matched by email, case-insensitively. |

### Response

| Field | Type | Description |
| :---- | :---- | :---- |
| `tasks` | array of [Data erasure task objects](#data-erasure-task-object) | Matching tasks. |

## GetTask

Returns one erasure task with its status and counters. Use it to follow a task started by [Create](#create) or [Upload](#upload).

`GET` `/api/data_erasure/tasks/{task_id}`

### Response

| Field | Type | Description |
| :---- | :---- | :---- |
| `task` | [Data erasure task object](#data-erasure-task-object) | The requested task. |

## ListTaskRows

Returns the per-identifier outcome of one task: what was deleted, not found, skipped, or failed. Identifiers are reported as their sha256 hash.

`GET` `/api/data_erasure/tasks/{task_id}/rows`

### Query parameters

| Parameter | Type | Description |
| :---- | :---- | :---- |
| `limit` / `offset` | integer | Paging. |

### Response

| Field | Type | Description |
| :---- | :---- | :---- |
| `rows` | array of [Data erasure task row objects](#data-erasure-task-row-object) | This page of results. |

## GetTaskReport

Returns the whole report of one task as a CSV file: one row per identifier per application, with its outcome. [ListTaskRows](#listtaskrows) shows the same rows page by page instead.

`GET` `/api/data_erasure/tasks/{task_id}/report`

### Response

| Field | Type | Description |
| :---- | :---- | :---- |
| `filename` | string | Suggested file name, for example `data-erasure-task-42.csv`. |
| `content_type` | string | `text/csv`. |
| `content` | string (base64) | The CSV file. Columns: `task_id`, `created`, `identifier_type`, `identifier_hash`, `application_code`, `result`, `detail`. |

## GetJournalReport

Returns every erasure of a period as one CSV file: a row per identifier per application, with the task and its initiator on each row. This is what an account hands to an auditor.

`GET` `/api/data_erasure/report`

### Query parameters

| Parameter | Type | Required | Description |
| :---- | :---- | :---- | :---- |
| `from` | string (RFC 3339) | Yes | Start of the period. |
| `to` | string (RFC 3339) | No | End of the period. Defaults to now. |
| `includeDryRuns` | boolean | No | Include dry-run tasks. |

<Aside type="caution">
A period with more than 2,000 erasure tasks is refused instead of being cut short. Narrow the period and call again.
</Aside>

### Response

| Field | Type | Description |
| :---- | :---- | :---- |
| `filename` | string | Suggested file name, for example `data-erasure-2026-01-01-2026-04-01.csv`. |
| `content_type` | string | `text/csv`. |
| `content` | string (base64) | The CSV file. Columns: `task_id`, `task_created`, `initiator_user_id`, `initiator_email`, `initiator_token_id`, `dry_run`, `identifier_type`, `identifier_hash`, `application_code`, `result`, `detail`, `row_created`. |

## Data erasure task object

| Field | Type | Description |
| :---- | :---- | :---- |
| `task_id` | integer | Task id. |
| `identifier_type` | string | `SUBJECT_IDENTIFIER_USER_ID`, `SUBJECT_IDENTIFIER_HWID`, or `SUBJECT_IDENTIFIER_EMAIL`. |
| `application_codes` | array of strings | Applications the task covers. |
| `dry_run` | boolean | Whether this task only measured a volume. |
| `status` | string | `ERASURE_TASK_STATUS_PENDING`, `_IN_PROGRESS`, `_DONE`, or `_FAILED`. |
| `total_count` | integer | Identifiers × applications this task covers. |
| `deleted_count` | integer | How many were erased (or, for a dry run, would be). |
| `not_found_count` | integer | How many matched nothing. |
| `failed_count` | integer | How many failed. See [ListTaskRows](#listtaskrows) or the report for why. |
| `fail_reason` | string | Set when `status` is `ERASURE_TASK_STATUS_FAILED`. |
| `initiator_user_id` | integer | Control Panel user who started the task. `0` when an API token did. |
| `initiator_token_id` | integer | API token that started the task. `0` when a Control Panel user did. |
| `initiator_email` | string | Address of the Control Panel user who started the task. Empty for an API token. |
| `created` | string (RFC 3339) | When the task was created. |
| `started_at` | string (RFC 3339) | When erasure work began. |
| `finished_at` | string (RFC 3339) | When the task reached a final status. |

## Data erasure task row object

| Field | Type | Description |
| :---- | :---- | :---- |
| `identifier_hash` | string | sha256 of the identifier. The report never stores or returns the identifier itself. |
| `application_code` | string | Application this row's outcome applies to. |
| `result` | string | `ERASURE_ROW_RESULT_DELETED`, `_NOT_FOUND`, `_SKIPPED`, or `_FAILED`. |
| `detail` | string | Technical detail, mainly set on failure. |
| `created` | string (RFC 3339) | When this row was written. |

## Related

<CardGrid>
  <LinkCard title="Data requests (product guide)" href="/product/account-management-and-security/data-requests/" />
  <LinkCard title="Erase personal data from User Explorer" href="/product/audience-data-and-segmentation/user-explorer/#erase-personal-data" />
</CardGrid>