---
title: Mark a passport for deletion
description: Step one of deleting a passport, before DELETE. Battery passports need a reason; refused while a newer second-life passport links to it. Paid plans only.
canonical: "https://www.tracepass.eu/docs/mark-passport-for-deletion"
locale: en
source: "https://www.tracepass.eu/docs/mark-passport-for-deletion"
---

# Mark a passport for deletion

> Step one of deleting a passport, before DELETE. Battery passports need a reason; refused while a newer second-life passport links to it. Paid plans only.

```http
POST /api/v1/passports/{id}/mark-for-deletion
```

**Step one of two.** Deleting a passport takes two steps: mark it for deletion here, then call `DELETE /api/v1/passports/{id}` (a never-published `draft` or `in_review` passport can be deleted directly). The delete is a soft delete: nothing is erased, but the passport disappears from the API and from every public surface. Scanning its QR code shows a page saying the passport has ended, and its machine-readable formats answer `410 Gone`.

A marked passport can no longer be edited, and marking a published passport frees its plan slot. A passport in any status except archived can be marked; marking it a second time answers `409 already_marked_for_deletion`. Fires the `passport.marked_for_deletion` webhook.

**Battery passports need a reason.** Under the Battery Regulation a battery passport ends only once the battery has been recycled (Art. 77(8)), so the request names why this one ends: `recycled`, `never_placed` (never placed on the market) or `duplicate_or_error`. Without one, a battery passport answers `400 reason_required`. For other categories `reason` is optional; when given, it must be one of the same three values.

**Refused while a newer passport links to it.** While a newer second-life battery passport in your account names this one as its predecessor, the mark answers `409 has_live_successor`; delete the newer passport first.

Deletion through the API needs a paid plan: on Free the mark answers `403 deletion_not_on_plan`. Counts as one v1 write. Honours `Idempotency-Key`. `POST /api/v1/passports/by-serial/{serial}/mark-for-deletion` is the by-serial alternate (it accepts the `?gtin=` disambiguator).

## Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `Authorization` | header | string | yes | `Bearer <token>` — either a `tp_` API key (Developer → API Keys; simplest, for server-to-server) or an OAuth 2.0 access token (Developer → OAuth Apps; for user-authorized apps, scoped + revocable). The Authentication page has the full OAuth flow and scope list. |
| `Idempotency-Key` | header | string | no | UUID v4. |
| `id` | path | string (24 hex chars) | yes | Passport ID. |
| `reason` | body | enum | no | `recycled`, `never_placed` or `duplicate_or_error`. Required for battery passports, optional for other categories. |

## Examples

```bash
curl -sS -X POST \
  https://app.tracepass.eu/api/v1/passports/6650b2c3d4e5f6a7b8c9d0e1/mark-for-deletion \
  -H "Authorization: Bearer tp_REDACTED_xxxxxxxxxxxx" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: 4f9c2a7e-1b3d-4c5e-8f60-7a8b9c0d1e2f" \
  -d '{ "reason": "recycled" }'
```

```typescript
const res = await fetch(
  `https://app.tracepass.eu/api/v1/passports/${id}/mark-for-deletion`,
  {
    method: "POST",
    headers: {
      Authorization: `Bearer ${process.env.TRACEPASS_API_KEY}`,
      "Content-Type": "application/json",
    },
    body: JSON.stringify({ reason: "recycled" }),
  },
);
if (!res.ok) {
  const { error, message } = await res.json();
  throw new Error(`${error}: ${message}`);
}
// Step two: DELETE /api/v1/passports/{id}
```

```python
import os, requests
res = requests.post(
    f"https://app.tracepass.eu/api/v1/passports/{passport_id}/mark-for-deletion",
    headers={"Authorization": f"Bearer {os.environ['TRACEPASS_API_KEY']}"},
    json={"reason": "recycled"},
)
res.raise_for_status()
# Step two: DELETE /api/v1/passports/{passport_id}
```

## Responses

### 200 — Marked for deletion

```json
{
  "_id": "6650b2c3d4e5f6a7b8c9d0e1",
  "status": "marked_for_deletion",
  "markedForDeletionAt": "2026-11-01T10:00:00.000Z",
  "markedForDeletionReason": "recycled"
}
```

### 400 — Reason required

```json
{
  "error": "reason_required",
  "message": "A reason is required for battery passports: send { \"reason\": \"recycled\" | \"never_placed\" | \"duplicate_or_error\" }. A battery passport ends only when the battery has been recycled."
}
```

### 403 — Plan gate

```json
{
  "error": "deletion_not_on_plan",
  "message": "Deletion is available on every paid plan; archive the passport instead."
}
```

### 409 — Newer passport links to it

```json
{
  "error": "has_live_successor",
  "message": "This passport has a live successor. Delete the successor first before marking this passport for deletion."
}
```

### 404 — Not found

```json
{
  "error": "passport_not_found",
  "message": "Passport not found."
}
```

## Related

- [Delete a passport (step two)](https://www.tracepass.eu/docs/delete-passport.md)
- [Archive a passport (irreversible)](https://www.tracepass.eu/docs/archive-passport.md)
