---
title: "PRD-027 — cancel a pending account deletion on the owner's behalf"
method: POST
path: "/api/v1/admin/accounts/{user_id}/cancel-deletion"
tags: ["admin"]
---

# PRD-027 — cancel a pending account deletion on the owner's behalf

`POST /api/v1/admin/accounts/{user_id}/cancel-deletion`

## Request

- Base URL: `https://api.badgeia.com`
- URL: `https://api.badgeia.com/api/v1/admin/accounts/{user_id}/cancel-deletion`
- Auth: none declared

Cancel a scheduled erasure. The recovery path for a hijacked account.

Self-service cancel-by-login is the normal reversal, and it is enough right
up until the operator has to suspend the account — which is exactly what
happens when a stolen session requests deletion and the real owner mails
support. Suspension freezes the attacker AND removes the owner's ability to
sign in and cancel, so without this endpoint the recovery action and the
only reversal are mutually exclusive.

Deliberately separate from `/enable`. Re-enabling an account is not consent
to cancel an erasure its owner genuinely asked for, and conflating them
would let a routine un-suspension silently revoke a real deletion request.
An operator resolving a takeover calls both; an operator lifting an
unrelated suspension calls one.

Leaves `suspended_at` alone: whether the account should also be
un-suspended is a separate judgement, made with `/enable`.

Idempotent: an account with no pending deletion returns 200 `changed=false`.

**Deliberately NOT gated on `ACCOUNT_DELETION_ENABLED`** — the one place in
this feature that ignores the flag. Rule #4 says a flag-off surface fails
explicit, and the reasoning holds for every endpoint that *starts* an
erasure. This one only ever stops one. The state it exists to clear can
outlive the flag: an operator flipping the flag off mid-grace leaves
accounts pending-and-frozen forever, and gating the unstick behind the
same flag would make the emergency stop unrecoverable. Admin-only, and it
cannot destroy anything. `test_admin_cancel_works_while_the_flag_is_off`
pins this as a decision rather than an oversight.

## Path parameters

- `user_id` string, uuid, required

## Response `200`

Successful Response

- object

## Other responses

- `422`: Validation Error

---

[API](https://skmtc.dev/badgeia/apis/badge.md) · [All operations](https://skmtc.dev/badgeia/apis/badge/llms.txt) · [OpenAPI document](https://skmtc.dev/badgeia/apis/badge/revisions/7e7f7fee8b77?raw)
