# Account Deletion — Design Spec

**Date:** 2026-04-27
**Scope:** Expo app + API

## Summary

Add account deletion to the Expo app. Deletion is immediate in terms of auth access (Supabase auth user is hard-deleted), but data is retained in the database (soft delete via `deleted_at`). No purge cron — orphaned data can be cleaned up manually later.

## API

### Endpoint

`DELETE /users/me` — authenticated, added to `apps/api/src/routers/user.ts`.

### Logic

1. Fetch the user's `organization_id` from `profiles`
2. If `organization_id` is set, count profiles with the same `organization_id`
3. If count == 1 (sole member), set `deleted_at = now()` on the `organizations` row
4. Set `deleted_at = now()` on the user's `profiles` row
5. Call `supabase.auth.admin.deleteUser(userId)` to hard-delete the Supabase auth user (revokes all sessions immediately)
6. Return `200 OK`

### Error handling

- If the user's profile is not found, return `404`
- If the Supabase admin delete fails, return `500` and do not set `deleted_at` (keep the operation atomic)
- If the user has no `organization_id`, skip steps 2–3 and proceed normally

## Database

- Add `deleted_at` column (nullable `timestamptz`) to `profiles` table — requires a Supabase migration
- `organizations` table already exists (referenced by the API, but absent from current generated types which are stale); add `deleted_at` column (nullable `timestamptz`) via migration as well
- No changes to other tables — tablos, tasks, and events are orphaned but inaccessible once auth is revoked
- After migrations, regenerate types: `npx supabase gen types typescript > packages/shared-types/src/database.types.ts`

## UI (Expo)

### Location

New "Danger Zone" section in the Settings screen (`xtablo-expo/app/(app)/(tabs)/settings.tsx`), positioned above the Sign Out button.

### Flow

1. User taps "Delete Account" in the Danger Zone section
2. Native `Alert` — warning message: account will be permanently deleted; if they are the sole owner of an organization, the organization will also be deleted. Buttons: "Cancel" and "Delete my account"
3. On confirm, call `DELETE /users/me` with Bearer token via the existing `api` client
4. On success, call `signOut()` from the auth store to clear local state and navigate to login
5. On error, show a toast with a user-friendly message

### Pattern

Uses native `Alert` dialogs — consistent with the existing logout confirmation pattern. No new screen required.

## Out of Scope

- Purge cron / scheduled data cleanup (deferred)
- Account reactivation / grace period cancellation
- Blocking deletion for sole org owners (org is cascade-deleted instead)
- Email confirmation step
